• 第22章 声音与音乐(2)


    22.2.4 数字录音机——WaveInXXX波形输入设备函数的使用

    (1)录音常用的API

      ①waveInOpen(打开一个音频输入设备)

      ②waveInPrepareHeader(为一个即将在waveInAddBuffer中调用的输入缓冲区准备头部)

      ③waveInAddBuffer(添加一个输入用的数据缓冲区)

      ④waveInStart(开始录音)

      ⑤waveInReset(重置输入设备以便重新录音或关闭录音)

      ⑥waveInClose(关闭音频输入设备)等几个。

    (2)windows waveform方式实现录音要通过这么几步:(注意顺序!!)

      ①打开录音设备——waveInOpen()函数

        注意,调用之前要填写好wav头信息(包含采样率、采样位数等);还要设置好接收录音消息的窗口句柄。

      ②准备好录音缓存空间——waveInPrepareHeader()函数

      这一步为了准备好将要送入录音设备的缓存,以便之后可以供之使用。一般至少需要准备两块缓存。因为录音不能间断,当一块填满时没有时间等待你去送入下一块缓存,所以必须提前就准备好。注意这两个缓存使用的先后顺序遵循FIFO原则

      ③将缓存送入录音设备——waveInAddBuffer()函数

    将缓存送入录音设备,供之存入已录下的音频。开始录音时,应至少送入两块不同的缓存,即调用两次这个函数。之后为了不致录音产生间断,应保证至少有一块缓存在录音时为空,以备衔接。

      ④开始录音——waveInStart()函数

      当以上的工作都准备好时,便可调用此函数开始录音了。一旦调用,录音设备便立即录音并存入已经送来的缓存块内,当被送来的有多个缓存块时,按照FIFO的原则向缓存块内存入录音数据。如果是在利用回调函数来接收录音消息时,此函数执行之后可以执行一个while()循环,来等待录音设备录音。为了减少CPU使用率,可以在循环中加人sleepex(x,TRUE),x单位ms,TRUE必须要有。每个缓冲块存满时,将发送一个MM_WIM_DATA消息。

      ⑤停止录音,关闭设备——依次调用以下这些函数,来结束录音。

      waveInStop: 停止录音

      waveInReset:复位。  //会发送MM_WIM_DATA消息

      waveInClose:关闭设备想结束录音时,最好在这函数前调用一下waveInReset

    (3)录音时的消息通知

      ①MM_WIM_OPEN

        当执行waveInOpen函数时,并产生这个消息信号。代表录音设备已经打开。在这里可以自己设定一些操作,也可以没有操作。 

      ②MM_WIM_DATA

      当每块缓存块填满时或调用waveInReset函数时,产生这个消息,在这个消息中,应当完成这样的工作,以便录音连续进行:

           A、将存满的缓存块处理,例如存入文件,或送入其他设备;

           B、向录音设备送入新的缓存块;录音设备任何时刻应当拥有不少于2个的缓存块,以保证录音不间断性。

      ③MM_WIM_CLOSE

        当调用waveInclose函数时,会产生这个消息,代表录音设备关闭成功。这次回调函数调用中,可以执行相应的一些关闭文件保存信息等等的操作,自定义。

    【Record1程序】

     

    /*----------------------------------------------------
      RECORD1.C —— Waveform Audio Recorder
                     (c) Charles Petzold,1998
    ----------------------------------------------------*/
    #include <Windows.h>
    #include "resource.h"
    
    #pragma comment(lib,"WINMM.LIB")
    
    #define INP_BUFFER_SIZE      16384 //16K,即16*1024
    BOOL CALLBACK  DlgProc(HWND, UINT, WPARAM, LPARAM);
    
    TCHAR szAppName[] = TEXT("Record1");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInStance,
                        PSTR szCmdLine, int iCmdShow)
    {
        if (-1 == DialogBox(hInstance,TEXT("Record"),NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                                  szAppName, MB_ICONERROR);
        }
        return 0;
    }
    
    void ReverseMemory(BYTE* pBuffer, int iLength)
    {
        BYTE b;
        int i;
    
        for (i = 0; i <iLength /2; i++)
        {
            b = pBuffer[i];
            pBuffer[i] = pBuffer[iLength - 1 - i];
            pBuffer[iLength - 1 - i] = b;
        }
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        RECT  rect;
        static HWAVEIN hWaveIn;
        static HWAVEOUT hWaveOut;
        static PWAVEHDR pWaveHdr1, pWaveHdr2;
        static PBYTE pBuffer1, pBuffer2, pSaveBuffer,pNewBuffer;
        static TCHAR szMemError[]  = TEXT("Error allocating memory!");
        static TCHAR szOpenError[] = TEXT("Error opening waveform audio!");
        static WAVEFORMATEX waveform;
    
        static BOOL bRecording, bPlaying, bReverse, bPaused,bEnding,bTerminating;
        static DWORD dwDataLength, dwRepetitions = 1;
    
        switch (message)
        {
        case WM_INITDIALOG:
            GetWindowRect(hwnd, &rect);
            SetWindowPos(hwnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2, 
                                     (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2,
                                     rect.right- rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW);
            
            //分配两个波形头结构
            pWaveHdr1 = malloc(sizeof(WAVEHDR));  //WaveIn(Out)PrepareHeader、WaveInAddBuffe等函数的参数
            pWaveHdr2 = malloc(sizeof(WAVEHDR));
    
            //分配“保存缓冲区”(用来保存声音数据的缓冲区)
            pSaveBuffer = malloc(1);  //1个字节,以后可根据需要录制的声音长度重新realloc来改变缓冲区的大小    
            return TRUE;
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            /*-----------------------------录制按钮过程--------------------------------------*/
            case IDC_RECORD_BEG:
    
                //分配16K的声音数据缓冲区
                //一般至少需要准备两块缓存。因为录音不能间断,当一块填满时没有时间等待你去送入下一块缓存,所以必须提前就准备好
                pBuffer1 = malloc(INP_BUFFER_SIZE);
                pBuffer2 = malloc(INP_BUFFER_SIZE);
    
                if (!pBuffer1 || !pBuffer2)
                {
                    if (pBuffer1) free(pBuffer1);
                    if (pBuffer2) free(pBuffer2);
    
                    MessageBeep(MB_ICONEXCLAMATION);
                    MessageBox(hwnd, szMemError, szAppName, MB_ICONEXCLAMATION | MB_OK);
                    return TRUE;
    
                }
    
                //打开波形输入设备
                waveform.wFormatTag         = WAVE_FORMAT_PCM;
                waveform.nChannels         = 1;
                waveform.nSamplesPerSec  = 11025;  //采样率:11025Hz
                waveform.nAvgBytesPerSec = 11025;  //每秒数据传输的字节数=nBlockAlign*nSamplesPerSec
                waveform.nBlockAlign     = 1;      //块对齐 = nChannels*wBitsPerSample/8(字节)
                waveform.wBitsPerSample  = 8;      //采样大小,即声音的振幅(强度)
                waveform.cbSize          = 0;      //PCM,必须为0
                
                if (MMSYSERR_NOERROR !=waveInOpen(&hWaveIn, WAVE_MAPPER, &waveform, (DWORD)hwnd,
                               0,CALLBACK_WINDOW))    //发送MM_WIM_OPEN消息
                {
                    free(pBuffer1);
                    free(pBuffer2);
                    MessageBeep(MB_ICONEXCLAMATION);
                    MessageBox(hwnd, szOpenError, szAppName, MB_ICONEXCLAMATION | MB_OK);
                    return TRUE;
                }
    
                //创建并准备波形头结构
                pWaveHdr1->lpData            = pBuffer1;
                pWaveHdr1->dwBufferLength    = INP_BUFFER_SIZE; //缓冲区的大小,当该数据超过该值是会发送MM_WIM_DATA消息
                pWaveHdr1->dwBytesRecorded    = 0;  //己经录制的字节数
                pWaveHdr1->dwUser            = 0;
                pWaveHdr1->dwFlags            = 0;
                pWaveHdr1->dwLoops            = 1;
                pWaveHdr1->lpNext            = NULL; //次数
                pWaveHdr1->reserved            = 0;
    
                waveInPrepareHeader(hWaveIn, pWaveHdr1, sizeof(WAVEHDR));
    
                pWaveHdr2->lpData            = pBuffer2;
                pWaveHdr2->dwBufferLength    = INP_BUFFER_SIZE; //缓冲区的大小,当该数据超过该值是会发送MM_WIM_DATA消息
                pWaveHdr2->dwBytesRecorded    = 0;  //己经录制的字节数
                pWaveHdr2->dwUser            = 0;
                pWaveHdr2->dwFlags            = 0;
                pWaveHdr2->dwLoops            = 1;
                pWaveHdr2->lpNext            = NULL; //次数
                pWaveHdr2->reserved            = 0;
    
                waveInPrepareHeader(hWaveIn, pWaveHdr2, sizeof(WAVEHDR));
                return TRUE;
    
            /*---------------------------录制结束按钮过程------------------------------------*/
            case IDC_RECORD_END:
    
                //重置波形输入设备,所有等待录音的缓冲区标志为己完成,当前位置设为第0个缓冲区
                //即清掉尚在等待录音的缓冲区,然后将正在录音的缓冲区中数据发送MM_WIM_DATA给用户处理
                bEnding = TRUE;
                waveInReset(hWaveIn);  //送MM_WIM_DATA消息
                return TRUE;
    
            /*-----------------------------播放按钮过程--------------------------------------*/
            case IDC_PLAY_BEG:
                //打开波形输出设备
                waveform.wFormatTag = WAVE_FORMAT_PCM;
                waveform.nChannels = 1;
                waveform.nSamplesPerSec  = 11025;//采样率:11025Hz
                waveform.nAvgBytesPerSec = 11025;//每秒数据传输的字节数=nBlockAlign*nSamplesPerSec
                waveform.nBlockAlign     = 1;    //块对齐 = nChannels*wBitsPerSample/8(字节)
                waveform.wBitsPerSample  = 8;    //采样大小,即声音的振幅(强度)
                waveform.cbSize          = 0;     //PCM,必须为0
    
                if (MMSYSERR_NOERROR != waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveform, (DWORD)hwnd,
                        0, CALLBACK_WINDOW))  //发送MM_WOM_OPEN消息
                {
                    MessageBeep(MB_ICONEXCLAMATION);
                    MessageBox(hwnd, szOpenError, szAppName, MB_ICONEXCLAMATION | MB_OK);
                }
                return TRUE;
    
            /*-----------------------------暂停按钮过程--------------------------------------*/
            case IDC_PLAY_PAUSE:
                //暂停或重启音频输出设备
                if (!bPaused)
                {
                    waveOutPause(hWaveOut);
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Resume"));
                    bPaused = TRUE;
                }
                else
                {
                    waveOutRestart(hWaveOut);
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Pause"));
                    bPaused = FALSE;
                }
                return TRUE;
    
            /*----------------------------停止播放按钮过程------------------------------------*/
            case IDC_PLAY_END:
    
                //复位音频输出设备,为关闭做准备
                bEnding = TRUE;
                waveOutReset(hWaveOut);  //会发送MM_WOM_DONE消息
                return TRUE;
    
            /*----------------------------反转播放按钮过程------------------------------------*/
            case IDC_PLAY_REV:
                //反转“保存缓冲区”中的数据,并倒播音频
                bReverse = TRUE;
                ReverseMemory(pSaveBuffer, dwDataLength);
                SendMessage(hwnd, WM_COMMAND, IDC_PLAY_BEG, 0); //发送消息,开始播放
                return TRUE;
    
            /*----------------------------重复播放按钮过程------------------------------------*/
            case IDC_PLAY_REP:
    
                //将播放次数设备为无限次数
                dwRepetitions = -1;
                SendMessage(hwnd, WM_COMMAND, IDC_PLAY_BEG, 0); //发送消息,开始播放
                return TRUE;
    
            /*----------------------------快进播放按钮过程------------------------------------*/
            case IDC_PLAY_SPEED:
                //打开音频输出设备,并快播(2倍速度)
                waveform.wFormatTag         = WAVE_FORMAT_PCM;
                waveform.nChannels         = 1;
                waveform.nSamplesPerSec  = 22050;//2倍的录音时的采样率(11025Hz)
                waveform.nAvgBytesPerSec = 22050;//每秒数据传输的字节数=nBlockAlign*nSamplesPerSec
                waveform.nBlockAlign     = 1;    //块对齐 = nChannels*wBitsPerSample/8(字节)
                waveform.wBitsPerSample  = 8;    //采样大小,即声音的振幅(强度)
                waveform.cbSize             = 0;     //PCM,必须为0
    
                if (MMSYSERR_NOERROR != waveOutOpen(&hWaveOut, 0, &waveform, (DWORD)hwnd, 0, CALLBACK_WINDOW))
                {
                    MessageBeep(MB_ICONEXCLAMATION);
                    MessageBox(hwnd, szOpenError, szAppName, MB_ICONEXCLAMATION | MB_OK);
                }
                return TRUE;
    
            }
            break;
            
        /*----------录制函数的消息,共3个(MM_WIM_OPEN、MM_WIM_DATA、MM_WIM_CLOSE)------------*/
        case MM_WIM_OPEN:
            //将“保存缓冲区”缩小为1个字节
            pSaveBuffer = realloc(pSaveBuffer, 1);
    
            //启动或禁用按钮组
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REV), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REP), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_SPEED), FALSE);
            SetFocus(GetDlgItem(hwnd, IDC_RECORD_END));
    
            //向波形输入设备增加缓冲区
            waveInAddBuffer(hWaveIn, pWaveHdr1, sizeof(WAVEHDR));
            waveInAddBuffer(hWaveIn, pWaveHdr2, sizeof(WAVEHDR));
    
            //开始采样
            bRecording = TRUE;
            bEnding = FALSE;
            dwDataLength = 0;
            //录音开始,录音设备便立即录音并存入已经送来的缓存块内,当被送来的有多个缓存块时,
            //按照FIFO的原则向缓存块内存入录音数据
            waveInStart(hWaveIn); 
            return TRUE;
    
        case MM_WIM_DATA: //当指定16K缓冲区满时会收到该消息
            //新“保存缓冲区”大小 = 原大小+新录制的大小
            pNewBuffer = realloc(pSaveBuffer, dwDataLength + ((PWAVEHDR)lParam)->dwBytesRecorded);
            if (pNewBuffer == NULL)
            {
                waveInClose(hWaveIn); //发送MM_WIM_CLOSE消息
                MessageBeep(MB_ICONEXCLAMATION);
                MessageBox(hwnd, szMemError, szAppName, MB_ICONEXCLAMATION | MB_OK);
                return TRUE;
            }
    
            pSaveBuffer = pNewBuffer;
            //将新录制的声音数据拷贝到新缓冲区后面。注意上面的realloc会保存原有的数据
            CopyMemory(pSaveBuffer + dwDataLength, ((PWAVEHDR)lParam)->lpData,
                           ((PWAVEHDR)lParam)->dwBytesRecorded);
            dwDataLength += ((PWAVEHDR)lParam)->dwBytesRecorded;
    
            if (bEnding)   //按了“End”按钮来结束录制时
            {
                waveInClose(hWaveIn);
                return TRUE;
            }
    
            //将当前使用的缓冲区作为新的缓冲区,发送给输入设备(该缓冲区将被重新覆盖录音)
            //注意即使不发送新的缓冲区给输入设备,录音也还在进行,只是数据被丢弃了。
            waveInAddBuffer(hWaveIn, (PWAVEHDR)lParam, sizeof(WAVEHDR));
            return TRUE;
    
        case MM_WIM_CLOSE:
            //释放缓冲区
            waveInUnprepareHeader(hWaveIn, pWaveHdr1, sizeof(WAVEHDR));
            waveInUnprepareHeader(hWaveIn, pWaveHdr2, sizeof(WAVEHDR));
    
            free(pBuffer1);
            free(pBuffer2);
    
            //启用或禁用相关的按钮
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
            SetFocus(GetDlgItem(hwnd, IDC_RECORD_BEG));
    
            if (dwDataLength>0)
            {
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REV), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REP), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_SPEED), TRUE);
            }
    
            bRecording = FALSE;
    
            if (bTerminating)   //如果是按下了关闭按钮
                SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
    
            return TRUE;
    
        /*----------播放函数的消息,共3个(MM_WOM_OPEN、MM_WOM_DONE、MM_WOM_CLOSE)------------*/
        case MM_WOM_OPEN:
            //启用或禁用相应按钮
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REV),   FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REP),   FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_SPEED), FALSE);
            SetFocus(GetDlgItem(hwnd, IDC_PLAY_END));
    
            //设置wav头信息
            pWaveHdr1->lpData = pSaveBuffer;
            pWaveHdr1->dwBufferLength = dwDataLength;
            pWaveHdr1->dwBytesRecorded = 0;
            pWaveHdr1->dwUser = 0;
            pWaveHdr1->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
            pWaveHdr1->dwLoops = dwRepetitions; //播放次数
            pWaveHdr1->lpNext = NULL;
            pWaveHdr1->reserved = 0;
    
            //准备并写入输出设备,播放完会发送MM_WOM_DONE消息
            waveOutPrepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));
            waveOutWrite(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));  //播放开始
    
            bEnding = FALSE;
            bPlaying = TRUE;
            return TRUE;
    
        case MM_WOM_DONE:
            waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));
            waveOutClose(hWaveOut);  //发送MM_WOM_CLOSE消息
            return TRUE;
    
        case MM_WOM_CLOSE:
            //启用或禁用相应按钮
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REV),   TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_REP),   TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_PLAY_SPEED), TRUE);
            SetFocus(GetDlgItem(hwnd, IDC_PLAY_BEG));
    
            SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Pause"));
            bPaused = FALSE;
            dwRepetitions = 1;
            bPlaying = FALSE;
    
            if (bReverse)  //如果以前是倒播的,现在重新恢复过来
            {
                ReverseMemory(pSaveBuffer, dwDataLength);
                bReverse = FALSE;
            }
    
            if (bTerminating)  //如果是按了“关闭”按钮
                SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
    
            return TRUE;
    
        case WM_SYSCOMMAND:
            switch (LOWORD(wParam))
            {
            case SC_CLOSE:
    
                if (bRecording)
                {
                    bTerminating = TRUE;
                    bEnding = TRUE;
                    waveInReset(hWaveIn);  //发送MM_WIM_DATA消息
                    return TRUE;
                }
    
                if (bPlaying)
                {
                    bTerminating = TRUE;
                    bEnding = TRUE;
                    waveOutReset(hWaveOut);  //发送MM_WOM_CLOSE消息
                    return TRUE;
                }
    
                if (pWaveHdr1) free(pWaveHdr1);
                if (pWaveHdr2) free(pWaveHdr2);
                if(pSaveBuffer) free(pSaveBuffer);
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
        return FALSE;
    }

    //resource.h

    //{{NO_DEPENDENCIES}}
    // Microsoft Visual C++ 生成的包含文件。
    // 供 Record1.rc 使用
    //
    #define IDC_RECORD_BEG                  1001
    #define IDC_RECORD_END                  1002
    #define IDC_PLAY_BEG                    1003
    #define IDC_PLAY_PAUSE                  1004
    #define IDC_PLAY_END                    1005
    #define IDC_PLAY_REV                    1006
    #define IDC_PLAY_REP                    1007
    #define IDC_PLAY_SPEED                  1008
    
    // Next default values for new objects
    // 
    #ifdef APSTUDIO_INVOKED
    #ifndef APSTUDIO_READONLY_SYMBOLS
    #define _APS_NEXT_RESOURCE_VALUE        102
    #define _APS_NEXT_COMMAND_VALUE         40001
    #define _APS_NEXT_CONTROL_VALUE         1002
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif

    //record.rc

    // Microsoft Visual C++ generated resource script.
    //
    #include "resource.h"
    
    #define APSTUDIO_READONLY_SYMBOLS
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 2 resource.
    //
    #include "winres.h"
    
    /////////////////////////////////////////////////////////////////////////////
    #undef APSTUDIO_READONLY_SYMBOLS
    
    /////////////////////////////////////////////////////////////////////////////
    // 中文(简体,中国) resources
    
    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
    LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
    
    #ifdef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // TEXTINCLUDE
    //
    
    1 TEXTINCLUDE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Dialog
    //
    
    RECORD DIALOGEX 0, 0, 181, 96
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
    CAPTION "Waveform Audio Recorder"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        PUSHBUTTON      "Record",IDC_RECORD_BEG,43,17,37,16
        PUSHBUTTON      "End",IDC_RECORD_END,97,17,37,16
        PUSHBUTTON      "Play",IDC_PLAY_BEG,20,43,37,16
        PUSHBUTTON      "Pause",IDC_PLAY_PAUSE,72,43,37,16
        PUSHBUTTON      "End",IDC_PLAY_END,124,43,37,16
        PUSHBUTTON      "Reverse",IDC_PLAY_REV,20,66,37,16
        PUSHBUTTON      "Repeat",IDC_PLAY_REP,72,66,37,16
        PUSHBUTTON      "SpeedUp",IDC_PLAY_SPEED,124,66,37,16
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        "RECORD", DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 174
            TOPMARGIN, 7
            BOTTOMMARGIN, 89
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED

    22.2.5 使用MCI的录音机

    (1)MciSendCommand函数

    参数

    说明

    MCIDEVICEID IDDevice

    接受命令的设备ID,则MCI_OPEN命令的wDeviceID变量返回

    UINT uMsg

    //指定如何控制设备,详细请查阅“MCI指令”列表

    DWORD fdwCommand

    Flags列表区,一般都与dwParam中的相关成配合使用

    DWORD dwParam

    一般是一个数据结构,程序在使用mciSendCommand命令访问MCI时需要的或者是返回的一些信息

    (2)MCI命令列表——详细请看这里《MciSendCommand用法详解》

       MCI_OPEN命令——打开当前设备,并返回设备ID(保存在MCI_OPEN_PARMS结构体中)

    ★★★★MCI_OPEN_PARMS结构体★★★

    字段

    说明

    dwCallback

    低位字为MCI_NOTIFY flag 指定一个窗口句柄

    wDeviceID

    成功打开的文件返回设备的标识符。(注意MCI_OPEN命令中的第1个参数为NULL,返回的ID将保存在该字段中)

    lpstrDeviceType

    设备类型的名字或常量标识。

    MCI_ALL_DEVICE_ID——所有设备

    MCI_DEVTYPE_ANIMATION——动画设备(Animation)

    MCI_DEVTYPE_CD_AUDIO——CD音频(cdaudio)

    MCI_DEVTYPE_DAT——数字音频(dat)

    MCI_DEVTYPE_DIGITAL_VIDEO——数字视频(digitalvideo)

    MCI_DEVTYPE_OTHER——未定义设备(other)

    MCI_DEVTYPE_OVERLAY——重叠视频(overlay)

    MCI_DEVTYPE_SCANNER——扫描仪(scanner)

    MCI_DEVTYPE_SEQUENCER——序列器(sequencer MIDI)

    MCI_DEVTYPE_VCR——合式录像机(vcr)

    MCI_DEVTYPE_VIDEODISC——激光视盘(videodisc)

    MCI_DEVTYPE_WAVEFORM_AUDIO——音频(waveaudioWave)

    lpstrElementName

    设备元素 (通常是打开的文件全路径).

    lpstrAlias

    可选别名.

    ★★★★MCI_OPEN中可用的FLAGS★★★

    适用范围

    Flags

    说明

    通用

    MCI_OPEN_ALIAS

    对应 MCI_OPEN_PARMS结构中的 LPCSTR lpstrAlias 成员变量,为打开的设备起个别名

    MCI_OPEN_SHAREABLE

    将设备或文件以共享的方式打开

    MCI_OPEN_TYPE

    对应 MCI_OPEN_PARMS结构中的 LPCSTR lpstrDeviceType 成员变量,设备类型的名字或常量标识. (设备名可以从注册表或者 SYSTEM.INI 文件中获得)

    MCI_OPEN_TYPE_ID

    和 MCI_OPEN_TYPE flag 一起使用后,我们可以在 MCI_OPEN_PARMS结构中的 lpstrDeviceType 成员变量的低字中得到一个标准的MCI 设备的类型ID,同时在高字中指出该设备ID 此时在系统里的顺序索引号。

    compound devices

    MCI_OPEN_ELEMENT

    对应 MCI_OPEN_PARMS结构中的 LPCSTR lpstrElementName 成员变量,设备元素 (通常是打开的文件全路径).

    MCI_OPEN_ELEMENT_ID

    和 MCI_OPEN_ELEMENT flag 一起使用后,我们可以将 MCI_OPEN_PARMS结构中的 lpstrElementName 成员变量看成是一个doubleword value,表示一个内部的设备。

    数字视频

    MCI_DGV_OPEN_NOSTATIC、MCI_DGV_OPEN_PARENT、 MCI_DGV_OPEN_WS、 MCI_DGV_OPEN_16BIT、MCI_DGV_OPEN_32BIT

    重叠视频

    MCI_OVLY_OPEN_PARENT、MCI_OVLY_OPEN_WS

    音频设备

    MCI_WAVE_OPEN_BUFFER

    对应 MCI_WAVE_OPEN_PARMS、(替代了MCI_OPEN_PARMS)结构中的 DWORD dwBufferSeconds 成员变量,用来设置读写音频的数据缓冲长度。

      ②MCI_SAVE:保存当前文件在保存文件操作之前,设备可以修改当前打开的文件,但不会影响到原来保存在磁盘上的原文件

    ★★★★MCI_SAVE_PARMS结构体★★★

    字段

    含义

    dwCallback

    目标窗口句柄或回调函数

    lpfilename

    指出存盘目标文件名

    ★★★★MCI_SAVE中可用的FLAGS标志★★★

    MCI_SAVE_FILE

    应用于所有设备。在MCI_SAVE_PARMS结构体中的 lpfilename 成员指出存盘目标文件名

     

      ③MCI_PLAY:播放当前的声音文件

    ★★★★MCI_PLAY_PARMS结构体★★★

    字段

    含义

    dwCallback

    低位字为MCI_NOTIFY标志,指定一个窗口句柄.

    dwFrom

    播放起始点

    dwTo

    播放结束点

    ★★★★MCI_PLAY中可用的FLAGS标志★★★

    FLAGS

    说明

    MCI_FROM

    让MCI_PLAY 使用上面结构中dwFrom。可以使用 MCI_SET 命令的MCI_SET_TIME_FORMAT标志设定时间格式。缺省播放会从当前位置开始

    MCI_TO

    用法和MCI_FROM 相同,播放结束于dwTo。缺省停止位置是媒体的结束位置

     

    MCI_RECORD:从当前位置开始或在一个指定区段中录音

    ★★★★MCI_RECORD_PARMS结构体★★★

    字段

    含义

    dwCallback

    低位字为MCI_NOTIFY标志,指定一个窗口句柄.

    dwFrom

    播放起始点

    dwTo

    播放结束点

    ★★★★MCI_RECORD中可用的FLAGS标志★★★

    FLAGS

    说明

    MCI_FROM|MCI_TO

    使MCI_RECORD_PARMS结构体中的dwFrom,dwTo起作用。如果没有使用这两个标志,MCI_RECORD缺省的起始录音位置是当前位置;缺省的结束位置

    MCI_RECORD_INSERT

    最新的录音会被插入或粘贴到当前的wave文件中。这是MCI_RECORD命令缺省设置,但有些设备可能不支持此标志。

    MCI_RECORD_OVERWRITE

    最新录音将覆盖现存打开的文件。MCIWAVE.DRV 设备返回MCIERR_UNSUPPORTED_FUNCTION 响应这个标志。

     

      ⑤MCI_SET:设置设备信息(设置时间格式及播放速度等)

    ★★★★MCI_SET_PARMS结构体★★★

    字段

    含义

    dwCallback

    低位字为MCI_NOTIFY标志,指定一个窗口句柄.

    dwTimeFormat

    设置时间格式

    时间格式常量:

    MCI_FORMAT_BYTES                比特(使用 脉冲编码调制[PCM]格式的waveaudio 类型文件)

    MCI_FORMAT_MILLISECONDS         设置时间单位为毫秒

    MCI_FORMAT_MSF                  (分 / 秒 / 帧)Minute / second / frame

    MCI_FORMAT_SAMPLES              采样 Samples

    MCI_FORMAT_SMPTE_24             SMPTE(电影与电视工程师学会[美]), 24 帧

    MCI_FORMAT_SMPTE_25             SMPTE, 25 帧

    MCI_FORMAT_SMPTE_30             SMPTE, 30 帧

    MCI_FORMAT_SMPTE_30DROP         SMPTE, 30 frame drop

    MCI_FORMAT_TMSF                 Track / minute / second / frame

    MCI_SEQ_FORMAT_SONGPTR          MIDI song pointer

    dwAudio

    音频输出声道编号(相关FLAG:MCI_SET_AUDIO)

    注意:对于waveform-audio设备,使用的是MCI_WAVE_SET_PARMS结构体 

    ★★★★MCI_SET中可用的FLAGS标志★★★

    FLAGS

    说明

    MCI_SET_AUDIO

    使 dwAudio 成员起作用。该flag必须配合MCI_SET_ON 或 MCI_SET_OFF使用

    MCI_SET_AUDIO_ALL、

    MCI_SET_AUDIO_LEFT、

    MCI_SET_AUDIO_RIGHT

    所有声道、左声道、右声道

    MCI_SET_DOOR_CLOSED、 MCI_SET_DOOR_OPEN

    关闭、打开设备面版(一般用在CD设备)

    MCI_SET_OFF、MCI_SET_ON

    设置视频信号(video signal)开关状态,与MCI_SET_VIDEO标志一起使用。

    MCI_SET_TIME_FORMAT

    设置时间格式

    MCI_FORMAT_HMS

    格式:时 / 分 / 秒

    【Record2程序】

    1、效果图:与 Record1一样,但Reverse、Repeat、SpeedUp等“特效”按钮不可用
    2、Resource.h和record.rc文件与Record1相同

    /*----------------------------------------------------
      RECORD2.C —— Waveform Audio Recorder
                     (c) Charles Petzold,1998
    ----------------------------------------------------*/
    #include <Windows.h>
    #include "resource.h"
    
    #pragma comment(lib,"WINMM.LIB")
    
    #define INP_BUFFER_SIZE      16384 //16K,即16*1024
    BOOL CALLBACK  DlgProc(HWND, UINT, WPARAM, LPARAM);
    
    TCHAR szAppName[] = TEXT("Record2");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInStance,
                        PSTR szCmdLine, int iCmdShow)
    {
        if (-1 == DialogBox(hInstance,TEXT("Record"),NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                                  szAppName, MB_ICONERROR);
        }
        return 0;
    }
    
    void ShowError(HWND hwnd, DWORD dwError)
    {
        TCHAR szErrorStr[1024];
    
        mciGetErrorString(dwError, szErrorStr, sizeof(szErrorStr) / sizeof(TCHAR));
        
        MessageBeep(MB_ICONEXCLAMATION);
        MessageBox(hwnd, szErrorStr, szAppName, MB_OK | MB_ICONEXCLAMATION);
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static BOOL bRecording, bPlaying, bPaused;
        static TCHAR szFileName[] = TEXT("record2.wav");
        static WORD  wDeviceID;
        DWORD  dwError;
    
        MCI_GENERIC_PARMS mciGeneric; //一般的结构体,只有dwCallback字段
        MCI_OPEN_PARMS    mciOpen;
        MCI_PLAY_PARMS    mciPlay;
        MCI_RECORD_PARMS  mciRecord;
        MCI_SAVE_PARMS    mciSave;
    
        RECT  rect;
    
        switch (message)
        {
        case WM_INITDIALOG:
            GetWindowRect(hwnd, &rect);
            SetWindowPos(hwnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2, 
                                     (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2,
                                     rect.right- rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW);
            return TRUE;
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            /*-----------------------------录制按钮过程--------------------------------------*/
            case IDC_RECORD_BEG:
                //删除己经存在的音频文件
                DeleteFile(szFileName);
                //打开音频输入设备
                mciOpen.dwCallback       = 0;
                mciOpen.wDeviceID         = 0;
                mciOpen.lpstrDeviceType  = TEXT("waveaudio"); //设备类型的名字或常量标识,标志位为MCI_OPEN_TYPE
                mciOpen.lpstrElementName = TEXT(""); //要设置为长度为0的字符串。但在播放时,可指定要播放的文件名。
                mciOpen.lpstrAlias       = NULL;
    
                //打开设备时,第1个参数为0,函数返回时,会自动填充mciOpen结构体的wDeviceID字段
                // MCI_WAIT  等待,直到输入设备完成所要求的行动才返回控制权
                // MCI_OPEN_TYPE 表示要使用MCI_OPEN_PARMS结构中的LpstrDiviceType参数
                // MCI_OPEN_ELEMENT 表示LpstrDeviceType参数中的是设备表述字符串
                // 还可以使用 MCI_OPEN_TYPE_ID 表示LpstrDeviceType参数中的是设备标识
                dwError = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, 
                                         (DWORD)(LPMCI_OPEN_PARMS)&mciOpen);
                if (dwError !=0)
                {
                    ShowError(hwnd, dwError);
                    return TRUE;
                }
    
                //保存己经打开的输入设备ID
                wDeviceID = mciOpen.wDeviceID;
    
                //开始录制
                mciRecord.dwCallback = (DWORD)hwnd;  //指定本窗口为接收消息的窗口
                mciRecord.dwFrom = 0;
                mciRecord.dwTo = 0;
                
                // MCI_NOTIFY 标志导致录音结束后系统向mciRecord.dwCallback元素中
                //指定的窗口句柄发送一条MM_MCINOTIFY通知消息,如使用MCI_STOP或MCI_PAUSE
                //或录制时磁盘空间不足时会发送MM_MCINOTIFY消息。本例没有使用MCI_FROM和
                //MCI_TO标志,每次录制时新的声音数据都会被插入到文件开始的地方
                
                mciSendCommand(wDeviceID, MCI_RECORD, MCI_NOTIFY,
                                  (DWORD)(LPMCI_RECORD_PARMS)&mciRecord);
    
                //启用或禁用相关按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_RECORD_END));
    
                bRecording = TRUE;            
                return TRUE;
            /*---------------------------录制结束按钮过程------------------------------------*/
            case IDC_RECORD_END:
                //停止录音
                mciGeneric.dwCallback = 0;
                // MCI_STOP 命令消息用于停止当前的录音
                mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT, 
                                   (DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
    
                //保存声音数据到指定的文件中
                mciSave.dwCallback = 0;
                mciSave.lpfilename = szFileName;
                //MCI_SAVE对当前录制在临时文件中的声音进行保持
                //MCI_SAVE_FILE标志指出保存的文件名在mciSave.lpfileName字段指定中
                mciSendCommand(wDeviceID, MCI_SAVE, MCI_WAIT | MCI_SAVE_FILE, 
                                     (DWORD)(LPMCI_SAVE_PARMS)&mciSave);
    
                //MCI_CLOSE用于删除所有的临时文件,释放己经建立的内存块,并关闭输入设备
                mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, 
                                  (DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
    
                //启用或禁用相应的按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG),  TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END),  FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),    TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),    FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE),  FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_BEG));
    
                //录音结束标志
                bRecording = FALSE;
                return TRUE;
            /*-----------------------------播放按钮过程--------------------------------------*/
            case IDC_PLAY_BEG:
                //打开音频设备
                mciOpen.dwCallback            = 0;
                mciOpen.wDeviceID            = 0;
                mciOpen.lpstrDeviceType        = NULL;
                mciOpen.lpstrElementName    = szFileName; //要播放的文件名
                mciOpen.lpstrAlias            = NULL;
    
                dwError = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT, 
                                              (DWORD)(LPMCI_OPEN_PARMS)&mciOpen);
    
                if (dwError !=0)
                {
                    ShowError(hwnd, dwError);
                    return TRUE;
                }
    
                //保存设备ID
                wDeviceID = mciOpen.wDeviceID;
    
                //开始播放声音
                mciPlay.dwCallback = (DWORD)hwnd; //播放结束会发送MM_MCINOTIFY消息
                mciPlay.dwFrom     = 0;
                mciPlay.dwTo       = 0;
    
                mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, 
                                  (DWORD)(LPMCI_PLAY_PARMS)&mciPlay);
    
                //启用或禁用相关的按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), TRUE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_END));
    
                bPlaying = TRUE;
                return TRUE;
    
            /*-----------------------------暂停播放按钮过程--------------------------------------*/
            case IDC_PLAY_PAUSE:
                if (!bPaused)
                {
                    //暂停
                    mciGeneric.dwCallback = 0;
    
                    mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT,  //会发送MM_MCINOTIFY消息
                                    (DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Resume"));
                    bPaused = TRUE;
                }
                else
                {
                    mciPlay.dwCallback = (DWORD)hwnd;
                    mciPlay.dwFrom = 0;
                    mciPlay.dwTo = 0;
    
                    //没指定MCI_FROM和MCI_TO标志,默认会从当前位置开始
                    mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, 
                                   (DWORD)(LPMCI_PLAY_PARMS)&mciPlay);
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Pause"));  
                    bPaused = FALSE;
                }
                return TRUE;
    
            /*----------------------------停止播放按钮过程------------------------------------*/
            case IDC_PLAY_END:
                //停止播放
                mciGeneric.dwCallback = 0;
                mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT,    //会发送MM_MCINOTIFY消息
                               (DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
    
                //关闭音频设备
                mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT,
                                   (DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
    
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END), FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_BEG));
    
                bPlaying = FALSE;
                bPaused = FALSE;
    
                return TRUE;
    
            /*----------------------------反转播放按钮过程------------------------------------*/
            case IDC_PLAY_REV:
                return TRUE;
    
            /*----------------------------重复播放按钮过程------------------------------------*/
            case IDC_PLAY_REP:
                return TRUE;
    
            /*----------------------------快进播放按钮过程------------------------------------*/
            case IDC_PLAY_SPEED:        
                return TRUE;
    
            }
            break;
            
        /*----------录制或磁盘己满、停止播放或暂停播放或播放结束会产生该消息------------*/
        //MCI_STOP、MCI_PAUSE——当按下End或Pause按钮时,wParam为MCI_NOTIFY_ABORTED,己经在相应的按
        //                        钮过程中处理了相关的逻辑,在本消息中可以忽略。                          
        //MCI_PLAY——当声音播放完,wParam为MCI_NOTIFY_SUCCESSFUL.
        //MCI_RECORD——当磁盘空间不足时,wParam为MCI_NOTIFY_SUCCESSFUL。(严格来讲是不成功的)
    case MM_MCINOTIFY:
            switch (wParam)
            {
            case MCI_NOTIFY_SUCCESSFUL:
                if (bPlaying) //当播放完,会收到该消息
                    SendMessage(hwnd, WM_COMMAND, IDC_PLAY_END, 0);
                
                if (bRecording) //磁盘空间不足时
                    SendMessage(hwnd, WM_COMMAND, IDC_RECORD_END, 0);
    
                return TRUE;
            }
            break;
    
        case WM_SYSCOMMAND:
            switch (LOWORD(wParam))
            {
            case SC_CLOSE: //当关闭按钮时
                if (bRecording) //正在录制时,则停止录制
                     SendMessage(hwnd, WM_COMMAND, IDC_RECORD_END, 0);
                
                if (bPlaying) //如果正在播放时,则停止播放
                    SendMessage(hwnd, WM_COMMAND, IDC_PLAY_END, 0);
    
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
        return FALSE;
    }

    22.2.6 用MCI命令字符串的方法

    (1)此例是基于文本的MCI接口中,与上例基于消息的MCI接口程序的结构上对应很很好。其实MCI就是把命令串翻译成对应的命令消息和数据结构。

    (2)本例没有使用MM_MCINOTIFY消息(可以实现)。导致程序不知道什么时候波形文件播放结束(必手动按下End按钮)

    (3)MCI的open命令中用到alias关键词。这个关键词允许所有随后的MCI命令使用别名来指明设备。

    【Record3程序】

    /*----------------------------------------------------
      RECORD3.C —— Waveform Audio Recorder
                     (c) Charles Petzold,1998
    ----------------------------------------------------*/
    #include <Windows.h>
    #include "..\Record1\resource.h"  //Record1程序中的资源头文件
                                        //同时要导入Record.rc文件,方法:资源文件-》添加-》现有项
    
    #pragma comment(lib,"WINMM.LIB")
    
    BOOL CALLBACK  DlgProc(HWND, UINT, WPARAM, LPARAM);
    
    TCHAR szAppName[] = TEXT("Record3");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInStance,
                        PSTR szCmdLine, int iCmdShow)
    {
        if (-1 == DialogBox(hInstance,TEXT("Record"),NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                                  szAppName, MB_ICONERROR);
        }
        return 0;
    }
    
    BOOL mciExecute(LPCTSTR szCommand)
    {
        MCIERROR error;
        TCHAR szErrorStr[1024];
        //mciSendString返回0时表示成功。
        if (error = mciSendString(szCommand,NULL,0,NULL))
        {
            mciGetErrorString(error, szErrorStr, sizeof(szErrorStr) / sizeof(TCHAR));
    
            MessageBeep(MB_ICONEXCLAMATION);
            MessageBox(NULL, szErrorStr, szAppName, MB_OK | MB_ICONEXCLAMATION);
        }
        
        return error == 0;
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static BOOL bRecording, bPlaying, bPaused;
        TCHAR szFileName[] = TEXT("record3.wav");
        RECT  rect;
    
        switch (message)
        {
        case WM_INITDIALOG:
            GetWindowRect(hwnd, &rect);
            SetWindowPos(hwnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2, 
                                     (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2,
                                     rect.right- rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW);
            return TRUE;
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            /*-----------------------------录制按钮过程--------------------------------------*/
            case IDC_RECORD_BEG:
                //删除己经存在的音频文件
                DeleteFile(szFileName);
                //打开音频输入设备
                //alias指定音频设备别名为mysound,以后可以直接用这个别名来使用该设备
                if (!mciExecute(TEXT("open new type waveaudio alias mysound")))
                    return TRUE;
    
                //录制音频
                mciExecute(TEXT("record mysound"));
    
                //启用或禁用相关按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_RECORD_END));
    
                bRecording = TRUE;            
                return TRUE;
            /*---------------------------录制结束按钮过程------------------------------------*/
            case IDC_RECORD_END:
                //停止录音
                mciExecute(TEXT("stop mysound"));
                mciExecute(TEXT("save mysound record3.wav"));
                mciExecute(TEXT("close mysound"));    
    
                //启用或禁用相应的按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG),  TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END),  FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),    TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),    FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE),  FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_BEG));
    
                //录音结束标志
                bRecording = FALSE;
                return TRUE;
            /*-----------------------------播放按钮过程--------------------------------------*/
            case IDC_PLAY_BEG:
                //打开音频设备
                if (!mciExecute(TEXT("open record3.wav alias mysound")))
                    return TRUE;
    
                //开始播放声音
                
                mciExecute(TEXT("play mysound"));
    
                //启用或禁用相关的按钮
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG),   FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END),   TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), TRUE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_END));
    
                bPlaying = TRUE;
                return TRUE;
    
            /*-----------------------------暂停播放按钮过程--------------------------------------*/
            case IDC_PLAY_PAUSE:
                if (!bPaused)
                {
                    //暂停
                    mciExecute(TEXT("pause mysound"));
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Resume"));
                    bPaused = TRUE;
                }
                else
                {
                    mciExecute(TEXT("play mysound"));
                    SetDlgItemText(hwnd, IDC_PLAY_PAUSE, TEXT("Pause"));  
                    bPaused = FALSE;
                }
                return TRUE;
    
            /*----------------------------停止播放按钮过程------------------------------------*/
            case IDC_PLAY_END:
                //停止播放
                mciExecute(TEXT("stop mysound"));
                //关闭音频设备
                mciExecute(TEXT("close mysound"));
    
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_BEG), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_RECORD_END), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_BEG), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_PAUSE), FALSE);
                EnableWindow(GetDlgItem(hwnd, IDC_PLAY_END), FALSE);
                SetFocus(GetDlgItem(hwnd, IDC_PLAY_BEG));
    
                bPlaying = FALSE;
                bPaused = FALSE;
    
                return TRUE;
    
            /*----------------------------反转播放按钮过程------------------------------------*/
            case IDC_PLAY_REV:
                return TRUE;
    
            /*----------------------------重复播放按钮过程------------------------------------*/
            case IDC_PLAY_REP:
                return TRUE;
    
            /*----------------------------快进播放按钮过程------------------------------------*/
            case IDC_PLAY_SPEED:        
                return TRUE;
    
            }
            break;
    
        case WM_SYSCOMMAND:
            switch (LOWORD(wParam))
            {
            case SC_CLOSE: //当关闭按钮时
                if (bRecording) //正在录制时,则停止录制
                     SendMessage(hwnd, WM_COMMAND, IDC_RECORD_END, 0);
                
                if (bPlaying) //如果正在播放时,则停止播放
                    SendMessage(hwnd, WM_COMMAND, IDC_PLAY_END, 0);
    
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
        return FALSE;
    }

     

     

  • 相关阅读:
    bzoj3237[Ahoi2013] 连通图
    bzoj3075[Usaco2013]Necklace
    bzoj1876[SDOI2009] SuperGCD
    bzoj3295[Cqoi2011] 动态逆序对
    BestCoder#86 E / hdu5808 Price List Strike Back
    bzoj2223[Coci 2009] PATULJCI
    bzoj2738 矩阵乘法
    poj 1321 -- 棋盘问题
    poj 3083 -- Children of the Candy Corn
    poj 2488 -- A Knight's Journey
  • 原文地址:https://www.cnblogs.com/5iedu/p/4715214.html
Copyright © 2020-2023  润新知