zoukankan      html  css  js  c++  java
  • 第22章 声音与音乐(1)

    22.1 Windows和多媒体

    22.1.1 Windows中支持多媒体功能的API

    (1)底层接口:如波形音频输入、输出函数waveIn和waveOut前缀开头

                   MIDI输出设备midiOut函数

    (2)高层接口:

      ①以mci为前缀的7个函数。mci本身有两种,一种是向MCI发送消息。一种是向MCI发送文本字符串。

      ②MessageBeep和PlaySound等函数。

    22.1.2 TESTMCI程序

     效果图

    /*---------------------------------------------------------
       TESTMCI.C —— MCI Command String Tester
                     (c)Charles Petzold,1998
    ---------------------------------------------------------*/
    
    #include <Windows.h>
    #include "resource.h"
    
    #pragma comment(lib,"WINMM.lib")  //须用到WINMM.DLL的导入表
    
    #define ID_TIMER 1
    BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
    
    TCHAR szAppName[] = TEXT("TestMci");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
        if (-1==DialogBox(hInstance,szAppName,NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
        }
    
        return 0;
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static HWND hwndEdit;
        int iCharBeg, iCharEnd,iLineBeg,iLineEnd,iLine,iLength,iChar;
        TCHAR szCommand[1024], szReturn[1024], szError[1024],szBuffer[24];
        MCIERROR error;
        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,
                0, 0, SWP_NOZORDER | SWP_NOSIZE);
    
            hwndEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
            SetFocus(hwndEdit);
            return FALSE;  //手动设置焦点,应返回FALSE。否则会设在第1个拥有WS_TABSTOP控件上
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDOK:
                //计算文本框中被选中的开始和结束的行数
                SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iCharBeg, (LPARAM)&iCharEnd);//开始位置和结束位置
    
                iLineBeg = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharBeg, 0);
                iLineEnd = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharEnd, 0);
                
                //遍历所有选中的行(如果没有选中的,则默认会读取最后一行文本)
                for (iLine = iLineBeg; iLine<=iLineEnd; iLine++)
                {
                    //每次循环获取第iLine行文本,并在最末尾加上结束,如果是空行,则跳过。
    
                    //缓冲区第1个字为缓冲区字符个数(含),EM_GETLINE消息要求的
                    *(WORD*)szCommand = sizeof(szCommand) / sizeof(TCHAR); 
                    iLength = SendMessage(hwndEdit, EM_GETLINE, iLine, (LPARAM)szCommand);
                    szCommand[iLength] = '';
    
                    if (iLength == 0) continue; //跳过空行
    
                    //发送MCI命令
                    /*
                    MCIERROR mciSendString(// 可用mciGetErrorString获得错误的文本描述            
                                    LPCTSTR lpszCommand, // 指向以null结尾的命令字符串:”命令 设备[ 参数]”
                                    LPTSTR lpszReturnString,// 指向接收返回信息的缓冲区,为NULL时不返回信息
                                    UINT cchReturn, // 上述缓冲区的大小
                                    HANDLE hwndCallback);//在命令串中含notify时,它指定一个回调窗口的句柄,一般为NULL
    
                    */
                    error = mciSendString(szCommand, szReturn, sizeof(szReturn) / sizeof(TCHAR), hwnd);
    
                    //显示返回的消息文本
                    SetDlgItemText(hwnd, IDC_RETURN_STRING, szReturn);
    
                    //显示错误信息
                    mciGetErrorString(error, szError, sizeof(szError) / sizeof(TCHAR));
                    SetDlgItemText(hwnd, IDC_ERROR_STRING, szError);
                }
    
                //将插入符(Caret)放在选定行中最后一行的后面
                iChar = SendMessage(hwndEdit, EM_LINEINDEX, iLineEnd, 0); //EM_LINEINDEX获取第iLineEnd行
                                                                          //第1个字符的位置索引
    
                iChar += SendMessage(hwndEdit, EM_LINELENGTH, iCharEnd, 0); //获取指定位置的字符(iCharEnd)所在行的字符个数
                                                                            //iChar指定该行最后一个字符的后面。
                SendMessage(hwndEdit, EM_SETSEL, iChar, iChar); //选择该行最后一个字符
    
                //插入回车换行符
                SendMessage(hwndEdit, EM_REPLACESEL, FALSE, (LPARAM)TEXT("
    "));
    
                SetFocus(hwndEdit);
                return TRUE;
    
            case IDCANCEL:
                EndDialog(hwnd, 0);
                return TRUE;
                
            case IDC_MAIN_EDIT: //编辑框消息
                if (HIWORD(wParam) == EN_ERRSPACE) //请求的空间无法得到满足时
                {
                    MessageBox(hwnd, TEXT("Error control out of space."),
                        szAppName, MB_OK | MB_ICONINFORMATION);
                    return TRUE;
                }
                break;
            }
            break;
    
            /*
             lParam = (LONG) lDevID;//
             wParam = (WPARAM) wFlags:
             1、MCI_NOTIFY_ABORTED      2、MCI_NOTIFY_FAILURE
             3、MCI_NOTIFY_SUCCESSFUL   4、MCI_NOTIFY_SUPERSEDED
            */
        case MM_MCINOTIFY:
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), TRUE);
            wsprintf(szBuffer, TEXT("Device ID = %i"), lParam);
            SetDlgItemText(hwnd, IDC_NOTIFY_ID, szBuffer);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), TRUE);
    
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL), wParam & MCI_NOTIFY_SUCCESSFUL);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), wParam & MCI_NOTIFY_SUPERSEDED);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED), wParam & MCI_NOTIFY_ABORTED);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE), wParam & MCI_NOTIFY_FAILURE);
    
            SetTimer(hwnd, ID_TIMER, 5000, NULL);
            return TRUE;
    
        case WM_TIMER:
            KillTimer(hwnd, ID_TIMER);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE), FALSE);
            return TRUE;
    
        case WM_SYSCOMMAND:
            switch (LOWORD(wParam))
            {
            case SC_CLOSE:
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
        return FALSE;
    }

    //resource.h

    //{{NO_DEPENDENCIES}}
    // Microsoft Visual C++ 生成的包含文件。
    // 供 TestMci.rc 使用
    //
    #define IDC_MAIN_EDIT                   1001
    #define IDC_NOTIFY_ID                   1002
    #define IDC_NOTIFY_MESSAGE              1003
    #define IDC_NOTIFY_SUCCESSFUL           1004
    #define IDC_NOTIFY_ABORTED              1005
    #define IDC_NOTIFY_FAILURE              1006
    #define IDC_NOTIFY_SUPERSEDED           1007
    #define IDC_ERROR_STRING                1008
    #define IDC_RETURN_STRING               1009
    
    // 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         1006
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif

    //TestMci.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
    //
    
    TESTMCI DIALOGEX 0, 0, 279, 290
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "MCI Tester"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        DEFPUSHBUTTON   "OK",IDOK,61,266,50,14
        PUSHBUTTON      "Close",IDCANCEL,146,266,50,14
        EDITTEXT        IDC_MAIN_EDIT,16,15,248,101,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL
        LTEXT           "Return String:",IDC_STATIC,19,118,46,8
        EDITTEXT        IDC_ERROR_STRING,144,130,120,67,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_GROUP | NOT WS_TABSTOP
        LTEXT           "Error String:",IDC_STATIC,145,119,40,8
        EDITTEXT        IDC_RETURN_STRING,18,129,120,66,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_GROUP | NOT WS_TABSTOP
        GROUPBOX        "MM_MCINOTIFY message",IDC_STATIC,18,199,247,62
        LTEXT           "",IDC_NOTIFY_ID,30,214,228,8
        LTEXT           "MCI_NOTIFY_SUCCESSFUL",IDC_NOTIFY_SUCCESSFUL,34,230,88,8,WS_DISABLED
        LTEXT           "MCI_NOTIFY_ABORTED",IDC_NOTIFY_ABORTED,163,230,78,8,WS_DISABLED
        LTEXT           "MCI_NOTIFY_FAILURE",IDC_NOTIFY_FAILURE,163,244,74,8,WS_DISABLED
        LTEXT           "MCI_NOTIFY_SUPERSEDED",IDC_NOTIFY_SUPERSEDED,34,244,89,8,WS_DISABLED
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        "TESTMCI", DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 272
            TOPMARGIN, 7
            BOTTOMMARGIN, 283
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED

    22.2 波形音频

    22.2.1 声音与波形

    (1)正弦波

       振幅:指声音强度

       频率:指音调(人类耳朵从低音调到高音调的区间范围为20Hz~20 000Hz)

    (2)人类对频率的感知是和频率的对数成正比的——频率翻倍定义为八度

    (3)傅里叶级数和正弦波谐波:任何周期性的波形,都可以分解为多个正弦波,它们的频率关系是整数倍数。基波的频率代表正弦波的频率,基波也被称为一次谐波,还有二次、三次等谐波。

    22.2.2 采样率和采样的大小

    (1)采样率:指每秒对波形采集的样本数量,必须是声音最高频率的2倍。这被称为“奈奎斯将频率”。如人耳能听到高达20kHz的声音,为了获取人类可以听到的整个音频范围,采样率为40kHz(是声音最高频率的2倍),但考虑到经过低通滤波后有衰减效应,所以提高10%左右,即44kHz。一般取44.1kHz。

    (2)采样大小:单位是位(bit)——用于表示声音的强度,决定了最小的声音和最大声音之间的差别。

    以分贝来表示——dB = 20*log(A1/A2),其中A1和A2是两个声音的振幅。

      ①采样大小为1位,即A1/A2 =1,则动态范围dB=0,因为只有一个振幅。

      ②采样大小为8位,即A1/A2 =256,动态范围dB= 20*log(256),即48分贝。其范围相当于一个安静的房间与一台运行着的电动机之间的区别。

      ③采样大小为16位时,产生的动态范围为dB=20*log(65536),即96分贝,这个范围非常接近听力最低阈值和产生痛感阈值之间的区别。注意,立体声是双声道的,每个声道采样大小为16位

    (3)计算未压缩音频所需占用的空间:声音长度*采样率*采样大小/8 *声道数 (单位:字节)

    22.2.3 用软件生成正弦波——WaveOutXXX波形输出设备函数的使用

    22.2.3.1 程序结构

    (1)本程序生成20Hz~5000Hz的正弦波

    (2)在脉冲编码时,采样率是一个常数,本例采用11025Hz,设要生成的正弦波的频率为X,则正弦波的每个周期内采集的样本数量为11025/x。由于每个周期的样本数量可能是小数,这样每个周期末尾都不连续。所以不能用此方法来生成正弦波。

    (3)正确的方法:——相位角。第1个样本为0度,递增值为正弦波频率*2π/采样率。如用11025Hz的采样率生成1000Hz的正弦波,则每个周期大约11个样本,用这11个样本的正弦值来表示每个样本的位数(振幅)。

    22.2.3.2程序用到函数或结构体

    (1)WAVEFORMATEX结构体

    字段

    含义

    WORD  wFormatTag

    指定格式类型; 默认 WAVE_FORMAT_PCM = 1

    WORD  nChannels

    指出波形数据的通道数; 单声道为 1, 立体声为 2

    DWORD nSamplesPerSec

    指定样本采样率(每秒的样本数)

    DWORD nAvgBytesPerSec

    指定数据传输的平均速率(每秒的字节数)

    PCM编码:其值等于nSamplesPerSec*nBlockAlign

    WORD  nBlockAlign

    指定块对齐, 块对齐是数据的最小单位。

    PCM编码:其值等于nChannels*wBitsPerSample/8(单位字节),即每个样本占用的字节总数,16 位立体声 PCM 的块对齐是 4 字节(每个样本2字节, 2个通道)

    WORD  wBitsPerSample

    采样大小(字节),等于8位或16位

    WORD  cbSize

    附加信息大小; PCM 格式没这个字段,可设为0。(PCM:脉冲编码调制)

    (2)waveOutOpen函数介绍

    参数

    含义

    LPHWAVEOUT phwo

    指向接收波形音频输出装置的句柄。如果fdwFlags设定为WAVE_FORMAT_QUERY时,该参数可为NULL

    UINT uDeviceID

    ①将要被打开的波形音频输出装置的ID,该值是个索引值,范围从0至系统中波形输出设备的数量减1.

    ②波形输出设备数量可用WaveOutGetNumDevs查询

    如果该值设为WAVE_MAPPER(-1),则会自动选择,一般会选择控制面板的多媒体程序的【音频】选项卡中【首选设备】中指定的设备,如果无法满足需求,会自动选择另一个设备。

    LPWAVEFORMATEX pwfx

    指向一个WAVEFORMATEX结构体的指针

    DWORD dwCallback

    指明接收波形输出消息的窗口句柄或回调函数。注意:回调函数是在中断时间内访问的, 必须在 DLL 中。要访问的数据都必须是在固定的数据段中。

    DWORD dwCallbackInstance

    如果dwCallBack指明了使用回调函数,则该参数可以指定要传递给回调函数额外的数据。

    DWORD fdwFlags

    CALLBACK_WINDOW、CALLBACK_FUNCTION:指定第4个参数的类型。

    CALLBACK_EVENT:指定dwCallback为事件句柄

    CALLBACK_THREAD:指定dwCallback为线程ID

    WAVE_ALLOWSYNC:当是同步设备时必须指定

    WAVE_FORMAT_QUERY:检查设备是否可以打开,而不实际去打开它。

    【返回值】

      ①MMSYSERR_NOERROR     = 0;  {成功时}

      ②MMSYSERR_BADDEVICEID = 2;  {设备ID超界}

      ③MMSYSERR_ALLOCATED   = 4;  {指定的资源已被分配}

      ④MMSYSERR_NODRIVER    = 6;  {没有安装驱动程序}

      ⑤MMSYSERR_NOMEM       = 7;  {不能分配或锁定内存}

      ⑥WAVERR_BADFORMAT     = 32; {设备不支持请求的波形格式}

    说明:

      ①函数调用后立即返回

      ②如果选择窗口句柄为接收波形输出消息,则会收到WM_WOM_OPEN消息,wParam为波形输出设备句柄,lParam为保留值(无用)

    (3)WaveOutPrepareHeader函数及WAVEHDR结构体

      ①WaveOutPrepareHeader函数:准备波形数据,可避免WAVEHDR结构体和缓冲区从内存被交换到磁盘去。

      ②WAVEHDR结构体

    字段

    含义

    LPSTR  lpData;

    存储声音数据的缓冲区地址,要在堆里的,不能是函数的局部变量,MM_WOM_DONE消息的处理函数中还要用到。

    DWORD  dwBufferLength

    上述缓冲区的大小

    DWORD  dwBytesRecorded

    当设备用于录音时,标志已经录入的数据长度

    DWORD  dwUser

    给应用程序用的

    DWORD  dwFlags;

    波形数据的缓冲区的属性

    WHDR_BEGINLOOP:指明该缓冲区是每次循环的第一个缓冲区。

    WHDR_ENDLOOP:指明该缓冲区是每次循环的最后一个缓冲区。

    WHDR_DONE:指明缓冲区数据播放完毕后,被设备驱动程序设为该值。

    DWORD  dwLoops

    循环播放的次数

    struct wavehdr_tag * lpNext

    保留值

    DWORD  reserved

    保留值

    (4)waveOutWrite函数——向指定的波输形输出设备传入数据,真正开始播放声音

      播放完后发送一条:MM_WOM_DONE消息,wParam为波形输出设备的句柄,lParam为指向WAVEHDR结构的指针。

    (5)waveOutReset函数——停止声音播放,然后将当前位置置0,所有等待播放的数据被标志为Done,并发送MM_WOM_DONE消息

      ①waveOutReset不是立即返回的函数,要等待驻留在波形输出设备中的数据处理完才返回。

      ②在调用到返回之间的这段时间内,禁止任何线程对处理reset中的hWaveOut调用,否则会造成死锁。

    (6)waveOutClose函数——发送MM_WOM_CLOSE消息

    (7)waveOutUnprepareHeader——清除由 waveOutPrepareHeader 完成的准备。

    //SineWave.c

    #include <Windows.h>
    #include <math.h>
    #include "resource.h"
    
    #pragma comment(lib,"WINMM.lib")
    
    #define SAMPLE_RATE      11025
    #define FREQ_MIN         20
    #define FREQ_MAX         5000
    #define FREQ_INIT         440
    #define PI                 3.1415926
    #define OUT_BUFFER_SIZE  4096
    
    TCHAR szAppName[] = TEXT("SineWave");
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        PSTR szCmdLine, int nCmdShow)
    {
        if (-1==DialogBox(hInstance,szAppName,NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                szAppName, MB_ICONERROR);
        }
        return 0;
    }
    
    void FillBuffer(PBYTE pBuffer, int iFreq)
    {
        static double fAngle;
        int i;
    
        //用相位角来生成正弦波声音数据,声音强度(振幅)从0-254
        for  (i= 0; i<OUT_BUFFER_SIZE; i++)
        {
            pBuffer[i] = (BYTE)(127+127*sin(fAngle));
    
            //相位增量为:正弦波频率*2π/采样率
            fAngle += 2 * PI * iFreq / SAMPLE_RATE;
    
            if (fAngle>2 * PI)
                fAngle -= 2 * PI; //注意这里不能重置为0,因为要让声音连续。
        }
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static BOOL bShutOff, bClosing;
        static HWND hwndScroll;
        static int iFreq = FREQ_INIT;
        static PBYTE  pBuffer1, pBuffer2; //用双缓冲播放声音
        static PWAVEHDR pWaveHdr1, pWaveHdr2;
        static WAVEFORMATEX waveformat;
        int iDummy;
    
        static HWAVEOUT hWaveOut;
    
        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);
    
            hwndScroll = GetDlgItem(hwnd, IDC_SCROLL);
            SetScrollRange(hwndScroll, SB_CTL, FREQ_MIN, FREQ_MAX, FALSE);
            SetScrollPos(hwndScroll, SB_CTL, FREQ_INIT, TRUE);
            SetDlgItemInt(hwnd, IDC_TEXT, FREQ_INIT, FALSE);
    
            //SendMessage(hwnd, DM_SETDEFID, (WPARAM)IDC_SCROLL, 0); //测试用的,改变对话框回车键默认行为
            return TRUE;
    
        case WM_HSCROLL:
            switch (LOWORD(wParam))
            {
            case SB_LINELEFT:       iFreq -= 1; break;
            case SB_LINERIGHT:        iFreq += 1; break;
            case SB_PAGELEFT:       iFreq /= 2; break;//降低1个八度
            case SB_PAGERIGHT:      iFreq *= 2; break;//升高1个八度
            case SB_THUMBTRACK:     iFreq = HIWORD(wParam);break;
            case SB_TOP:       
                GetScrollRange(hwndScroll, SB_CTL, &iFreq, &iDummy); //将最小值放入iFreq,最大放入iDummy
                break;
            case SB_BOTTOM:
                GetScrollRange(hwndScroll, SB_CTL, &iDummy, &iFreq); //将最大值放入iFreq
                break;
            }
    
            iFreq = max(FREQ_MIN, min(FREQ_MAX, iFreq));
    
            SetScrollPos(hwndScroll, SB_CTL, iFreq, TRUE);
            SetDlgItemInt(hwnd, IDC_TEXT, iFreq, FALSE);
            
            return TRUE;
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDC_ONOFF:
    
                //如果是要打开波形音频,则hWaveOut==NULL
                if (hWaveOut == NULL)
                {
                    //为2个声音头结构和声音数据分配内存
                    pWaveHdr1 = malloc(sizeof(WAVEHDR));
                    pWaveHdr2 = malloc(sizeof(WAVEHDR));
                    pBuffer1  = malloc(OUT_BUFFER_SIZE);
                    pBuffer2  = malloc(OUT_BUFFER_SIZE);
    
                    if (!pWaveHdr1 || !pWaveHdr2 || !pBuffer1 ||!pBuffer2)
                    {
                        if (!pWaveHdr1) free(pWaveHdr1);
                        if (!pWaveHdr2) free(pWaveHdr2);
                        if (!pBuffer1)  free(pBuffer1);
                        if (!pBuffer2)  free(pBuffer2);
    
                        MessageBeep(MB_ICONEXCLAMATION);
                        MessageBox(hwnd, TEXT("Error allocating memory!"),
                                    szAppName, MB_ICONEXCLAMATION | MB_OK);
                        return TRUE;
                    }
    
                    //关闭按钮按下标志
                    bShutOff = FALSE;
    
                    //打开波形输出设备
                    waveformat.wFormatTag        = WAVE_FORMAT_PCM;
                    waveformat.nChannels        = 1;//单声道
                    waveformat.nSamplesPerSec   = SAMPLE_RATE;
                    waveformat.nAvgBytesPerSec    = SAMPLE_RATE;//nSamplesPerSec*nBlockAlign
                    waveformat.nBlockAlign        = 1; //每个样本所占的字节数,nChannels*wBitsPerSample/8
                    waveformat.wBitsPerSample    = 8; //每个样本大小(位数)(单位:位)
                    waveformat.cbSize            = 0;
    
                    //自动选择波形输出设备,并打开。消息发送指定的窗口
                    if (MMSYSERR_NOERROR != waveOutOpen(&hWaveOut , WAVE_MAPPER , &waveformat,
                                                        (DWORD)hwnd, 0, CALLBACK_WINDOW ))
                    {
                        free(pWaveHdr1);
                        free(pWaveHdr2);
                        free(pBuffer1);
                        free(pBuffer2);
    
                        hWaveOut = NULL;
                        MessageBeep(MB_ICONEXCLAMATION);
                        MessageBox(hwnd, TEXT("Error opening waveform audio device!"),
                            szAppName, MB_ICONEXCLAMATION | MB_OK);
                        return TRUE;
                    }
    
                    //创建波形头结构并做好输出准备
                    pWaveHdr1->lpData            = pBuffer1;       //指向声音数据
                    pWaveHdr1->dwBufferLength    = OUT_BUFFER_SIZE;//声音数据的大小
                    pWaveHdr1->dwBytesRecorded  = 0;
                    pWaveHdr1->dwUser            = 0;
                    pWaveHdr1->dwFlags            = 0;
                    pWaveHdr1->dwLoops            = 1;
                    pWaveHdr1->lpNext            = NULL;
                    pWaveHdr1->reserved            = 0;
    
                    //防止WAVEHDR和缓冲区从内存被交换到磁盘去
                    waveOutPrepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));
    
                    pWaveHdr2->lpData            = pBuffer2;       //指向声音数据
                    pWaveHdr2->dwBufferLength    = OUT_BUFFER_SIZE;//声音数据的大小
                    pWaveHdr2->dwBytesRecorded    = 0;
                    pWaveHdr2->dwUser            = 0;
                    pWaveHdr2->dwFlags            = 0;
                    pWaveHdr2->dwLoops            = 1;
                    pWaveHdr2->lpNext            = NULL;
                    pWaveHdr2->reserved            = 0;
    
                    //防止WAVEHDR和缓冲区从内存被交换到磁盘去
                    waveOutPrepareHeader(hWaveOut, pWaveHdr2, sizeof(WAVEHDR));
                }
                else  //如果要关闭波形文件,则输出设备复位
                {
                    bShutOff = TRUE;
                    waveOutReset(hWaveOut);//停止播放
                }
                return TRUE;
            }
            break;
    
        case MM_WOM_OPEN: //调用waveOutOpen函数后,收到该消息
            SetDlgItemText(hwnd, IDC_ONOFF, TEXT("Turn Off"));
    
            //将两个缓冲区的数据写入波形输出设备中
            FillBuffer(pBuffer1, iFreq);
            waveOutWrite(hWaveOut, pWaveHdr1, sizeof(WAVEHDR)); //真正的播放开始
    
            FillBuffer(pBuffer2, iFreq);  //双缓冲技术,可以保证发出的声音是连续的。
            waveOutWrite(hWaveOut, pWaveHdr2, sizeof(WAVEHDR));
            return TRUE;
    
        case MM_WOM_DONE:  //当缓冲区数据播放完毕或waveOutReset时,收到该消息
            if (bShutOff)
            {
                waveOutClose(hWaveOut); //按下Turn Off按钮
                return TRUE;
            }
    
            //如果缓冲区的数据播放完毕,则
            //生成新的声音数据,并发送给波输形输出设备
            FillBuffer(((PWAVEHDR)lParam)->lpData, iFreq);
            waveOutWrite(hWaveOut, (PWAVEHDR)lParam, sizeof(WAVEHDR));
            return TRUE;
    
        case MM_WOM_CLOSE:
            waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));
            waveOutUnprepareHeader(hWaveOut, pWaveHdr2, sizeof(WAVEHDR));
    
            if (pWaveHdr1) free(pWaveHdr1);
            if (pWaveHdr2) free(pWaveHdr2);
            if (pBuffer1)  free(pBuffer1);
            if (pBuffer2)  free(pBuffer2);
    
            hWaveOut = NULL;
            SetDlgItemText(hwnd, IDC_ONOFF, TEXT("Turn On"));
    
            if (bClosing)
                EndDialog(hwnd, 0);
    
            return TRUE;
    
        case WM_SYSCOMMAND:
            switch (wParam)
            {
            case SC_CLOSE:
                if (hWaveOut !=NULL)
                {
                    bShutOff = TRUE;
                    bClosing = TRUE;
    
                    waveOutReset(hWaveOut);
                }
                else
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
    
        return FALSE;
    }

    //resource.h

    //{{NO_DEPENDENCIES}}
    // Microsoft Visual C++ 生成的包含文件。
    // 供 SineWave.rc 使用
    //
    #define IDC_SCROLL                      1001
    #define IDC_TEXT                        1002
    #define IDC_ONOFF                       1003
    
    // 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         1004
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif

    //SineWave.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
    //
    
    SINEWAVE DIALOGEX 0, 0, 247, 62
    STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
    CAPTION "Sine Wave Generator"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        DEFPUSHBUTTON   "Turn On",IDC_ONOFF,106,44,50,14
        SCROLLBAR       IDC_SCROLL,15,12,189,11,WS_TABSTOP
        RTEXT           "440",IDC_TEXT,202,13,25,8
        LTEXT           "Hz",IDC_STATIC,229,13,9,8,WS_TABSTOP
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        "SINEWAVE", DIALOG
        BEGIN
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED
  • 相关阅读:
    OpenCV之设计模式
    ca
    BMP结构详解
    Lucas–Kanade光流算法学习
    卡尔曼
    hash+链表
    文件操作
    查找
    fseek效率
    大家都说3C直连网络,3C网络是指什么呢?
  • 原文地址:https://www.cnblogs.com/5iedu/p/4715193.html
Copyright © 2011-2022 走看看