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

    22.2.7 波形音频文件格式——WAV音频格式

    偏移量

    字节

    数据

    0000

    4

    字符串——“RIFF”(资源交换文件格式),文件由很多数据“块”组成,每一块数据由块的名称和块长组成。名称由4个ASCII字符组成。块长不包含块的名称和块长这个字段本身所需的8个字节,也就是说是“块”的实际长度。

    0004

    4

    波形数据块的大小(文件大小-8)即从偏移0008开始到文件结束的大小

    0008

    4

    字符串——“WAVE”

    000C

    4

    字符串——“fmt ”(注意:fmt后面有一空格)

    0010

    4

    格式数据块的大小(16字节),即sizeof(PCMWAVEFORMATEX)

    0014

    2

    wf.wFormatTag = WAVE_FORMAT_PCM =1

    0016

    2

    wf.nChannels

    0018

    4

    wf.nSamplesPerSec

    001C

    4

    wf.nAvgBytesPerSec

    0020

    2

    wf.nBlockAlign

    0022

    2

    wf.wBitsPerSample //样本大小,当为8位及以下时,被解释为无符号整数,0x80表示无声。8位以上时无声为0

    0024

    4

    字符串——“data”

    0028

    4

    波形数据的大小,即真正的声音数据的大小

    002C

    n

    波形数据——声音数据

    22.2.8 使用加法合成来生成波形声音(小号、双簧管、单簧管)

    (1)每一种乐器由一系列的分音组成(小号12个分音,双簧管和单簧管各21个分音)

    (2)PRT结构:存放每个分音的振幅和频率包络线的点的个数和各振幅和各频率。

    (3)INS结构:为每种乐器的结构,存放乐音的总时间,分音的数量和所有分音振幅和频率。

    (4)FillBuffer:先计算累计最大振幅。这个振幅在后面用于把采样按比例缩放到8位的样本大小。接着,按指定的频率开始采样,然后对每个分音的振幅和频率进行线性插值。

    传入频率值和相位角,并调用SineGenerator函数正弦值并累加分音振幅,最后将振幅缩放到8位的样本大小。

    【AddSynth程序】

     

    /*------------------------------------------------------------------
    ADDSYNTH.C —— additive Synthesis Sound Generator声音的加法合成器
                   (c)Charles Petzold,1998
    -------------------------------------------------------------------*/
    #include <Windows.h>
    #include <math.h>
    #include "AddSynth.h"
    #include "resource.h"
    
    #pragma comment(lib,"WINMM.LIB")
    
    #define ID_TIMER      1        //计时器
    #define PI            3.14159
    #define SAMPLE_RATE   22050    //采样频率
    #define MAX_PARTIALS  21       //最大的泛音个数
    
    
    BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
    
    TCHAR szAppName[] = TEXT("AddSynth");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iShowCmd)
    {
        if (-1 ==DialogBox(hInstance,szAppName,NULL,DlgProc))
        {
            MessageBox(NULL, TEXT("This program require Windows NT!"), 
                            szAppName, MB_ICONERROR);
        }
        return 0;
    }
    
    //正弦波发生器
    //根据相位角,求正弦波的振幅。并根据频率给出下一个相位角
    double SineGenerator(double dFreq, double* pdAngle)
    {
        double dAmp; //振幅
        
        dAmp = sin(*pdAngle); //计算振幅
        *pdAngle += 2 * PI*dFreq / SAMPLE_RATE; //计算下一个样本的相位角
        if (*pdAngle >= 2 * PI)
            *pdAngle -= 2 * PI;
        return dAmp;  //返回振幅值
    }
    
    //用合成的波形数据填充缓冲区
    void FillBuffer(INS ins, PBYTE pBuffer, int iNumSamples)
    {
        static double dAngle[MAX_PARTIALS];//定义一个相位角数组
        double dAmp, dFrq, dComp, dFrac;  //定义振幅、频率、合成振幅、包络的线性插值比例因子
        int i, iPrt, iMsecTime, iCompMaxAmp, iMaxAmp, iSmp;//定义i、泛音数组下标、每个样本的时间(毫秒)
                                                           //最大合成振幅、最大振幅、样本号
    
        //计算合成的最大振幅
        iCompMaxAmp = 0;
    
        //在泛音集中循环以计算最大振幅
        for (iPrt = 0; iPrt < ins.iNumPartials; iPrt++)
        {
            iMaxAmp = 0;
            for (i = 0; i < ins.pprt[iPrt].iNumAmp; i++)
                iMaxAmp = max(iMaxAmp, ins.pprt[iPrt].pEnvAmp[i].iValue);
    
            iCompMaxAmp += iMaxAmp;
        }
    
        //循环每个样本
        for (iSmp = 0; iSmp < iNumSamples; iSmp++)
        {
            dComp = 0;
            iMsecTime = (int)(1000 * iSmp / SAMPLE_RATE);//取得每个样本的毫秒数
    
            //循环每个泛音集
            //以下循环在每个泛音中进行振幅和频率插值
            for (iPrt = 0; iPrt < ins.iNumPartials; iPrt++)
            {
                dAmp = 0;  //插值处的振幅
                dFrq = 0;  //插值处的频率
    
                //对每个泛音集在指定的时间处(iMsecTime),进行振幅的插值
                for (i = 0; i < ins.pprt[iPrt].iNumAmp-1; i++)
                {
                    if (iMsecTime >= ins.pprt[iPrt].pEnvAmp[i].iTime &&
                        iMsecTime <= ins.pprt[iPrt].pEnvAmp[i+1].iTime)
                    {
                        //计算插值比例
                        dFrac = (iMsecTime - ins.pprt[iPrt].pEnvAmp[i].iTime) /
                                (ins.pprt[iPrt].pEnvAmp[i + 1].iTime - ins.pprt[iPrt].pEnvAmp[i].iTime);
                        
                        //dAmp = v[i]+ dFrac*(v[i+1]-v[i]),即:
                        //利用前两后两个振幅的插值比例,计算出插值处的振辐
                        dAmp = dFrac* ins.pprt[iPrt].pEnvAmp[i + 1].iValue +
                            (1 - dFrac)*ins.pprt[iPrt].pEnvAmp[i].iValue;
    
                        break;
                    }
                }
    
                //对每个泛音集在指定的时间处(iMsecTime),进行频率的插值
                for (i = 0; i < ins.pprt[iPrt].iNumFrq - 1; i++)
                {
                    if (iMsecTime >= ins.pprt[iPrt].pEnvFrq[i].iTime &&
                        iMsecTime <= ins.pprt[iPrt].pEnvFrq[i + 1].iTime)
                    {
                        //计算插值比例
                        dFrac = (iMsecTime - ins.pprt[iPrt].pEnvFrq[i].iTime) /
                            (ins.pprt[iPrt].pEnvFrq[i + 1].iTime - ins.pprt[iPrt].pEnvFrq[i].iTime);
    
                        //dFrq = v[i]+ dFrac*(v[i+1]-v[i]),即:
                        //利用前两后两个振幅的插值比例,计算出插值处的振辐
                        dFrq = dFrac* ins.pprt[iPrt].pEnvFrq[i + 1].iValue +
                            (1 - dFrac)*ins.pprt[iPrt].pEnvFrq[i].iValue;
    
                        break;
                    }
                }
                //计算每个采样点处的合成振幅
                dComp += dAmp*SineGenerator(dFrq, dAngle + iPrt); 
            }
            pBuffer[iSmp] = (BYTE)(127 + 127 * dComp / iCompMaxAmp); //每个样本的振幅缩放到0-254之间
        }
    }
    
    //创建声音波形文件
    BOOL MakeWaveFile(INS ins, TCHAR* szFileName)
    {
        HANDLE hFile;
        PBYTE pBuffer; //缓冲区,用来接受一个音符的所有样本。
        int iNumSamples,iPcmSize,iChunkSize;
        WAVEFORMATEX waveform; //定义一个波形音频结构变量
        DWORD dwWritten;
    
        hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == NULL)
            return FALSE;
    
        //SAMPLE_RATE:采样率(样本数/秒),以下表示在iMsecTime毫秒内应采样的数量(取偶数个)
        iNumSamples = ((long)ins.iMsecTime*SAMPLE_RATE / 1000 + 1) / 2 * 2;
        iPcmSize = sizeof(PCMWAVEFORMAT);//取得脉冲编码调制结构体大小
        
        //文件大小-8,即从第3个字段开始的WAV文件(见课本P1020图22-6)
        //0008-0014:12节字(分别为“WAVE”、“fmt ”、“格式数据块大小”字段
        //0014-0024:PCMWAVEFORMAT结构体
        //0024-002C:“data”、波形数据大小字段
        //002C以后:波形数据(本例每个样本用1字节存储)
        iChunkSize = 12 + iPcmSize + 8 + iNumSamples; //除“RIFF”和“波形数据块大小”两个字段
    
        if (NULL == (pBuffer = malloc(iNumSamples)))
        {
            CloseHandle(hFile);
            return FALSE;
        }
    
        FillBuffer(ins, pBuffer, iNumSamples);
    
        //初始化waveform结构
        waveform.wFormatTag = WAVE_FORMAT_PCM; //脉冲编码调制格式
        waveform.nChannels = 1;//单声道
        waveform.nSamplesPerSec = SAMPLE_RATE; //采样率
        waveform.nAvgBytesPerSec = SAMPLE_RATE; //平均数据传输率=nSamplesPerSec*nBlockAlign
        waveform.nBlockAlign = 1;//以1字节对齐,=nChannels*wBitsPerSample/8
        waveform.wBitsPerSample = 8; //样本大小,用来表示样本的振幅,介于(0-255)之间
        waveform.cbSize = 0; //对于PCM,必须为0;
        
        //将波形声音数据写入文件(文件格式:WAV格式)
        WriteFile(hFile, "RIFF", 4, &dwWritten, NULL);      //"RIFF"
        WriteFile(hFile, &iChunkSize, 4, &dwWritten, NULL); //波形数据块的大小(文件大小-8),即W从AV文件第3字段开始
        WriteFile(hFile, "WAVEfmt ", 8, &dwWritten, NULL);  //第3、4字段:“WAVE”、“fmt ”(注意fmt后有一空格)
        WriteFile(hFile, &iPcmSize, 4, &dwWritten, NULL);   //格式数据块的大小,即WAVEFORMATEX结构体
        WriteFile(hFile, &waveform, sizeof(WAVEFORMATEX)-2, &dwWritten, NULL); //不含WAVEFORMATEX中的cbSize字段
        WriteFile(hFile, "data", 4, &dwWritten, NULL);       //“data”字段
        WriteFile(hFile, &iNumSamples, 4, &dwWritten, NULL); //波形数据的大小
        WriteFile(hFile, pBuffer, iNumSamples, &dwWritten, NULL);
    
        CloseHandle(hFile);
        free(pBuffer);
    
        if (dwWritten !=iNumSamples)
        {
            DeleteFile(szFileName);
            return FALSE;
        }
        return TRUE;
    }
    
    //测试和创建音频文件
    void TestAndCreate(HWND hwnd, INS ins, TCHAR* szFileName, int idButton)
    {
        TCHAR szMessage[64];
    
        //返回文件的属性,如果失败,返回-1
        if (-1 != GetFileAttributes(szFileName))
        {
            EnableWindow(GetDlgItem(hwnd, idButton), TRUE);
        }
        else
        {
            if (MakeWaveFile(ins,szFileName))
            {
                EnableWindow(GetDlgItem(hwnd, idButton), TRUE);
            }
            else
            {
                wsprintf(szMessage, TEXT("Could not create %x."), szFileName);
                MessageBeep(MB_ICONEXCLAMATION);
                MessageBox(hwnd, szMessage, szAppName, MB_OK | MB_ICONEXCLAMATION);
            }
        }
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static TCHAR* szTrum = TEXT("Trumpet.wav"); //小号
        static TCHAR* szOboe = TEXT("Oboe.wav");    //双簧管
        static TCHAR* szClar = TEXT("Clarinet.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);
    
            SetTimer(hwnd, ID_TIMER, 1, NULL); //设置定时器。目的是
            return TRUE;
    
        case WM_TIMER:
            KillTimer(hwnd, ID_TIMER);
            SetCursor(LoadCursor(NULL,IDC_WAIT));
            ShowCursor(TRUE);
    
            TestAndCreate(hwnd, insTrum, szTrum, IDC_TRUMPET);
            TestAndCreate(hwnd, insOboe, szOboe, IDC_OBOE);
            TestAndCreate(hwnd, insClar, szClar, IDC_CLARINET);
    
            SetDlgItemText(hwnd, IDC_TEXT, TEXT(""));
            SetFocus(GetDlgItem(hwnd, IDC_TRUMPET));
    
            ShowCursor(FALSE);
            SetCursor(LoadCursor(NULL, IDC_ARROW));
    
            return TRUE;
    
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDC_TRUMPET:
                PlaySound(szTrum, NULL, SND_FILENAME | SND_SYNC); //播放小号
                return TRUE;
    
            case IDC_OBOE:
                PlaySound(szOboe, NULL, SND_FILENAME | SND_SYNC); //播放双簧管
                return TRUE;
    
            case IDC_CLARINET:
                PlaySound(szClar, NULL, SND_FILENAME | SND_SYNC); //播放单簧管
                return TRUE;
            }
            break;
    
        case WM_SYSCOMMAND:
            switch (LOWORD(wParam))
            {
            case SC_CLOSE:
                EndDialog(hwnd, 0);
                return TRUE;
            }
            break;
        }
        return FALSE;
    }

    //AddSynth.h

    /*-------------------------------------------------------------------
       ADDSYNTH.H -- Amplitude and Frequency Envelopes for 3 Instruments
    
                     From "Computer Music Journal," Volume II, Number 2
      -------------------------------------------------------------------*/
    
    typedef struct
    {
         int iTime ;  //时间
         int iValue ; //该时间处,振幅的大小或该振幅出现的频率
    }
    ENV ;//定义一个声音包络结构
    
    typedef struct
    {
         int  iNumAmp ; //分音中振幅的数量
         ENV *pEnvAmp ; 
         int  iNumFrq ; //分音中频率的数量
         ENV *pEnvFrq ;
    }
    PRT ;//定义一个泛音结构
    
    typedef struct
    {
         int   iMsecTime ;
         int   iNumPartials ;
         PRT  *pprt ;
    }
    INS ;//定义一个乐器结构
    
    //以下定义小号的包络
    ENV envTrumAmp01[] = { 1, 0, 20, 305, 36, 338, 141, 288, 237, 80, 360, 0 };//第1个分音,格式:时间,相应的振幅 
    ENV envTrumFrq01[] = { 1, 321, 16, 324, 32, 312, 109, 310, 317, 314, 360, 310 };//第2个分音:格式:时间、及该时间处相应振幅出现的频率
    ENV envTrumAmp02[] = { 1, 0, 3, 0, 25, 317, 39, 361, 123, 295, 222, 40, 326, 0, 360, 0 };
    ENV envTrumFrq02[] = { 1, 0, 2, 0, 3, 607, 16, 657, 24, 621, 133, 621, 275, 628, 326, 628, 327, 0, 360, 0 };
    ENV envTrumAmp03[] = { 1, 0, 2, 0, 19, 100, 34, 369, 111, 342, 207, 41, 273, 0, 360, 0 };
    ENV envTrumFrq03[] = { 1, 0, 2, 977, 5, 782, 15, 987, 24, 932, 128, 932, 217, 936, 273, 945, 275, 0, 360, 0 };
    ENV envTrumAmp04[] = { 1, 0, 3, 0, 24, 113, 29, 257, 118, 231, 187, 35, 235, 0, 360, 0 };
    ENV envTrumFrq04[] = { 1, 0, 2, 0, 3, 718, 16, 1335, 24, 1243, 108, 1240, 199, 1248, 235, 1248, 236, 0, 360, 0 };
    ENV envTrumAmp05[] = { 1, 0, 27, 52, 34, 130, 110, 126, 191, 13, 234, 0, 360, 0 };
    ENV envTrumFrq05[] = { 1, 1225, 9, 1569, 12, 1269, 21, 1573, 37, 1553, 97, 1552, 181, 1556, 234, 1566, 235, 0, 360, 0 };
    ENV envTrumAmp06[] = { 1, 0, 46, 83, 64, 100, 100, 100, 189, 11, 221, 0, 360, 0 };
    ENV envTrumFrq06[] = { 1, 1483, 12, 1572, 23, 1988, 33, 1864, 114, 1864, 177, 1868, 221, 1879, 222, 0, 360, 0 };
    ENV envTrumAmp07[] = { 1, 0, 37, 39, 45, 77, 110, 79, 176, 11, 205, 0, 207, 0, 360, 0 };
    ENV envTrumFrq07[] = { 1, 1792, 9, 1612, 29, 2242, 36, 2174, 93, 2176, 126, 2170, 205, 2188, 207, 0, 360, 0 };
    ENV envTrumAmp08[] = { 1, 0, 2, 0, 28, 17, 43, 71, 109, 66, 172, 8, 201, 0, 360, 0 };
    ENV envTrumFrq08[] = { 1, 0, 2, 1590, 29, 2539, 36, 2491, 114, 2481, 153, 2489, 201, 2491, 203, 0, 360, 0 };
    ENV envTrumAmp09[] = { 1, 0, 2, 0, 29, 16, 43, 53, 54, 66, 105, 64, 165, 7, 191, 0, 360, 0 };
    ENV envTrumFrq09[] = { 1, 0, 2, 1993, 25, 2121, 32, 2821, 37, 2796, 84, 2798, 105, 2792, 191, 2797, 192, 0, 360, 0 };
    ENV envTrumAmp10[] = { 1, 0, 27, 6, 41, 25, 56, 29, 72, 22, 95, 24, 180, 0, 360, 0 };
    ENV envTrumFrq10[] = { 1, 1792, 12, 1849, 32, 3131, 37, 3111, 114, 3103, 164, 3116, 180, 3116, 181, 0, 360, 0 };
    ENV envTrumAmp11[] = { 1, 0, 2, 0, 37, 6, 55, 25, 88, 29, 114, 28, 164, 3, 186, 0, 360, 0 };
    ENV envTrumFrq11[] = { 1, 0, 2, 1398, 31, 3419, 42, 3419, 91, 3419, 106, 3406, 150, 3421, 186, 3421, 187, 0, 360, 0 };
    ENV envTrumAmp12[] = { 1, 0, 7, 0, 39, 3, 43, 8, 88, 11, 118, 9, 138, 3, 165, 0, 360, 0 };
    ENV envTrumFrq12[] = { 1, 0, 6, 0, 7, 1806, 23, 2942, 36, 2759, 37, 3746, 50, 3723, 84, 3731, 110, 3721, 156, 3741, 165, 3620, 167, 0, 360, 0 };
    
    //以下定义双簧管的包络
    ENV envOboeAmp01[] = { 1, 0, 9, 0, 14, 10, 26, 10, 52, 140, 94, 187, 153, 170, 313, 0 };
    ENV envOboeFrq01[] = { 1, 0, 8, 0, 9, 314, 25, 292, 43, 311, 144, 311, 272, 313, 313, 309 };
    ENV envOboeAmp02[] = { 1, 0, 10, 0, 26, 17, 40, 139, 159, 115, 239, 62, 307, 0, 313, 0 };
    ENV envOboeFrq02[] = { 1, 0, 9, 0, 10, 708, 16, 617, 41, 625, 105, 621, 265, 630, 307, 626, 308, 0, 313, 0 };
    ENV envOboeAmp03[] = { 1, 0, 10, 0, 25, 19, 36, 163, 71, 191, 129, 187, 297, 0, 313, 0 };
    ENV envOboeFrq03[] = { 1, 0, 9, 0, 10, 915, 21, 931, 72, 938, 148, 935, 249, 941, 297, 938, 299, 0, 313, 0 };
    ENV envOboeAmp04[] = { 1, 0, 10, 0, 25, 16, 43, 221, 64, 173, 114, 171, 284, 0, 313, 0 };
    ENV envOboeFrq04[] = { 1, 0, 9, 0, 10, 1209, 18, 1261, 37, 1246, 109, 1245, 238, 1255, 284, 1253, 285, 0, 313, 0 };
    ENV envOboeAmp05[] = { 1, 0, 6, 0, 13, 3, 21, 0, 28, 0, 44, 210, 59, 238, 126, 224, 199, 85, 292, 0, 313, 0 };
    ENV envOboeFrq05[] = { 1, 0, 5, 0, 6, 1553, 21, 1582, 25, 1237, 28, 1533, 35, 1564, 56, 1557, 113, 1555, 185, 1565, 292, 1566, 293, 0, 313, 0 };
    ENV envOboeAmp06[] = { 1, 0, 13, 0, 17, 1, 24, 0, 30, 0, 41, 63, 67, 40, 121, 38, 278, 0, 313, 0 };
    ENV envOboeFrq06[] = { 1, 0, 12, 0, 13, 1907, 22, 1883, 28, 1544, 30, 1856, 36, 1878, 52, 1871, 113, 1866, 169, 1878, 225, 1876, 278, 1891, 280, 0, 313, 0 };
    ENV envOboeAmp07[] = { 1, 0, 8, 0, 14, 0, 21, 0, 32, 0, 37, 22, 119, 12, 146, 3, 194, 8, 256, 0, 313, 0 };
    ENV envOboeFrq07[] = { 1, 0, 6, 0, 8, 1978, 21, 1923, 28, 1717, 32, 2191, 111, 2177, 188, 2193, 229, 2182, 256, 2194, 257, 0, 313, 0 };
    ENV envOboeAmp08[] = { 1, 0, 6, 0, 14, 0, 21, 0, 37, 0, 66, 5, 106, 3, 129, 4, 199, 3, 235, 0, 313, 0 };
    ENV envOboeFrq08[] = { 1, 0, 5, 0, 6, 2506, 21, 2491, 25, 1252, 37, 2523, 56, 2495, 110, 2489, 140, 2491, 195, 2502, 235, 2505, 237, 0, 313, 0 };
    ENV envOboeAmp09[] = { 1, 0, 4, 0, 14, 0, 20, 0, 36, 0, 45, 32, 78, 24, 132, 25, 161, 15, 241, 0, 313, 0 };
    ENV envOboeFrq09[] = { 1, 0, 2, 0, 4, 2783, 20, 2779, 29, 1286, 37, 2803, 80, 2806, 113, 2799, 167, 2813, 241, 2818, 242, 0, 313, 0 };
    ENV envOboeAmp10[] = { 1, 0, 6, 0, 17, 2, 22, 0, 35, 0, 47, 121, 144, 112, 206, 21, 242, 0, 313, 0 };
    ENV envOboeFrq10[] = { 1, 0, 5, 0, 6, 3123, 22, 3115, 29, 2229, 35, 3118, 70, 3117, 113, 3109, 200, 3130, 242, 3131, 243, 0, 313, 0 };
    ENV envOboeAmp11[] = { 1, 0, 5, 0, 17, 1, 24, 0, 37, 0, 47, 70, 123, 67, 167, 44, 188, 16, 239, 0, 313, 0 };
    ENV envOboeFrq11[] = { 1, 0, 4, 0, 5, 3285, 24, 3388, 29, 1270, 37, 3430, 76, 3429, 110, 3423, 194, 3444, 239, 3444, 241, 0, 313, 0 };
    ENV envOboeAmp12[] = { 1, 0, 14, 1, 24, 0, 37, 0, 44, 49, 79, 42, 122, 46, 185, 8, 231, 0, 313, 0 };
    ENV envOboeFrq12[] = { 1, 3627, 24, 3664, 29, 1690, 37, 3739, 90, 3742, 115, 3733, 187, 3760, 231, 3763, 233, 0, 313, 0 };
    ENV envOboeAmp13[] = { 1, 0, 4, 0, 16, 0, 24, 0, 40, 0, 47, 27, 84, 22, 126, 24, 177, 7, 229, 0, 313, 0 };
    ENV envOboeFrq13[] = { 1, 0, 2, 0, 4, 4081, 24, 4064, 30, 1350, 40, 4064, 57, 4049, 148, 4051, 181, 4074, 229, 4069, 230, 0, 313, 0 };
    ENV envOboeAmp14[] = { 1, 0, 4, 0, 16, 0, 21, 0, 41, 0, 44, 13, 63, 8, 82, 7, 111, 10, 175, 0, 313, 0 };
    ENV envOboeFrq14[] = { 1, 0, 2, 0, 4, 4321, 21, 4259, 29, 1238, 41, 4346, 61, 4367, 87, 4368, 102, 4357, 175, 4376, 176, 0, 313, 0 };
    ENV envOboeAmp15[] = { 1, 0, 47, 0, 72, 3, 97, 3, 121, 1, 161, 2, 175, 0, 313, 0 };
    ENV envOboeFrq15[] = { 1, 0, 45, 0, 47, 3164, 55, 4557, 68, 4662, 98, 4670, 142, 4661, 175, 4666, 176, 0, 313, 0 };
    ENV envOboeAmp16[] = { 1, 0, 48, 0, 61, 4, 86, 4, 126, 3, 137, 5, 161, 0, 313, 0 };
    ENV envOboeFrq16[] = { 1, 0, 47, 0, 48, 4567, 49, 4978, 75, 4990, 109, 4982, 138, 4985, 161, 4996, 163, 0, 313, 0 };
    ENV envOboeAmp17[] = { 1, 0, 51, 0, 61, 5, 76, 3, 132, 3, 164, 2, 173, 0, 313, 0 };
    ENV envOboeFrq17[] = { 1, 0, 49, 0, 51, 4634, 55, 5313, 66, 5301, 99, 5301, 129, 5292, 173, 5318, 175, 0, 313, 0 };
    ENV envOboeAmp18[] = { 1, 0, 52, 0, 63, 2, 91, 3, 126, 3, 156, 2, 168, 0, 313, 0 };
    ENV envOboeFrq18[] = { 1, 0, 51, 0, 52, 4729, 59, 5606, 92, 5611, 122, 5605, 152, 5611, 168, 5628, 169, 0, 313, 0 };
    ENV envOboeAmp19[] = { 1, 0, 47, 0, 56, 2, 80, 1, 117, 2, 159, 1, 176, 0, 313, 0 };
    ENV envOboeFrq19[] = { 1, 0, 45, 0, 47, 5772, 57, 5921, 86, 5928, 114, 5914, 150, 5938, 176, 5930, 177, 0, 313, 0 };
    ENV envOboeAmp20[] = { 1, 0, 49, 0, 57, 2, 83, 2, 109, 1, 159, 3, 195, 0, 313, 0 };
    ENV envOboeFrq20[] = { 1, 0, 48, 0, 49, 5369, 57, 6268, 76, 6230, 145, 6234, 184, 6263, 195, 6244, 196, 0, 313, 0 };
    ENV envOboeAmp21[] = { 1, 0, 57, 0, 61, 0, 88, 1, 113, 0, 129, 1, 140, 0, 313, 0 };
    ENV envOboeFrq21[] = { 1, 0, 56, 0, 57, 5477, 61, 6440, 71, 6550, 97, 6538, 122, 6554, 140, 6548, 141, 0, 313, 0 };
    
    //以下定义单簧管的包络
    ENV envClarAmp01[] = { 1, 0, 7, 0, 20, 6, 32, 73, 48, 445, 199, 361, 330, 0 };
    ENV envClarFrq01[] = { 1, 0, 6, 0, 7, 282, 19, 368, 21, 314, 46, 310, 141, 312, 284, 313, 330, 314 };
    ENV envClarAmp02[] = { 1, 0, 24, 0, 43, 22, 104, 2, 193, 4, 238, 10, 301, 0, 330, 0 };
    ENV envClarFrq02[] = { 1, 0, 23, 0, 24, 629, 68, 619, 116, 616, 167, 633, 223, 624, 301, 627, 302, 0, 330, 0 };
    ENV envClarAmp03[] = { 1, 0, 15, 0, 37, 12, 48, 159, 204, 122, 286, 17, 309, 0, 330, 0 };
    ENV envClarFrq03[] = { 1, 0, 14, 0, 15, 803, 24, 928, 36, 898, 46, 931, 113, 939, 330, 942 };
    ENV envClarAmp04[] = { 1, 0, 9, 0, 19, 2, 24, 0, 39, 0, 49, 26, 103, 3, 167, 5, 229, 10, 291, 0, 330, 0 };
    ENV envClarFrq04[] = { 1, 0, 7, 0, 9, 1261, 24, 1314, 30, 327, 39, 1245, 105, 1243, 215, 1257, 246, 1249, 291, 1261, 292, 0, 330, 0 };
    ENV envClarAmp05[] = { 1, 0, 6, 0, 18, 5, 25, 0, 39, 0, 54, 375, 212, 210, 266, 20, 295, 0, 330, 0 };
    ENV envClarFrq05[] = { 1, 0, 5, 0, 6, 1572, 25, 1528, 32, 911, 38, 1560, 67, 1554, 127, 1565, 308, 1569, 309, 0, 330, 0 };
    ENV envClarAmp06[] = { 1, 0, 3, 0, 11, 0, 15, 0, 41, 0, 48, 25, 108, 4, 216, 12, 282, 0, 330, 0 };
    ENV envClarFrq06[] = { 1, 0, 2, 0, 3, 1934, 12, 1890, 33, 320, 46, 1862, 186, 1883, 282, 1875, 283, 0, 330, 0 };
    ENV envClarAmp07[] = { 1, 0, 2, 0, 18, 1, 24, 0, 42, 0, 52, 108, 127, 46, 177, 42, 253, 0, 330, 0 };
    ENV envClarFrq07[] = { 1, 0, 2, 2180, 24, 2148, 34, 795, 43, 2167, 113, 2193, 253, 2192, 255, 0, 330, 0 };
    ENV envClarAmp08[] = { 1, 0, 2, 0, 14, 1, 23, 0, 43, 0, 52, 83, 110, 17, 199, 18, 242, 0, 330, 0 };
    ENV envClarFrq08[] = { 1, 0, 2, 2458, 23, 2343, 33, 328, 45, 2472, 125, 2507, 242, 2510, 243, 0, 330, 0 };
    ENV envClarAmp09[] = { 1, 0, 5, 0, 20, 2, 21, 3, 27, 0, 42, 0, 55, 127, 132, 73, 163, 71, 255, 0, 330, 0 };
    ENV envClarFrq09[] = { 1, 0, 3, 0, 5, 2849, 27, 2688, 33, 964, 42, 2792, 128, 2822, 255, 2819, 256, 0, 330, 0 };
    ENV envClarAmp10[] = { 1, 0, 5, 0, 23, 1, 30, 0, 47, 0, 54, 32, 92, 17, 232, 7, 247, 0, 330, 0 };
    ENV envClarFrq10[] = { 1, 0, 3, 0, 5, 3173, 30, 3030, 39, 2320, 50, 3096, 134, 3136, 247, 3138, 248, 0, 330, 0 };
    ENV envClarAmp11[] = { 1, 0, 23, 1, 28, 0, 39, 0, 59, 44, 122, 26, 153, 26, 262, 0, 330, 0 };
    ENV envClarFrq11[] = { 1, 3313, 28, 3279, 34, 1768, 43, 3420, 127, 3448, 262, 3441, 264, 0, 330, 0 };
    ENV envClarAmp12[] = { 1, 0, 10, 2, 21, 0, 46, 0, 52, 53, 158, 9, 206, 28, 255, 0, 330, 0 };
    ENV envClarFrq12[] = { 1, 3756, 21, 3728, 33, 2095, 47, 3741, 136, 3762, 255, 3759, 256, 0, 330, 0 };
    ENV envClarAmp13[] = { 1, 0, 3, 0, 16, 1, 29, 0, 41, 0, 46, 24, 52, 8, 77, 57, 192, 8, 250, 0, 330, 0 };
    ENV envClarFrq13[] = { 1, 0, 2, 0, 3, 4152, 29, 3868, 36, 2240, 46, 4045, 85, 4049, 128, 4078, 181, 4078, 250, 4103, 251, 0, 330, 0 };
    ENV envClarAmp14[] = { 1, 0, 3, 0, 16, 0, 20, 0, 48, 0, 56, 38, 110, 3, 188, 14, 228, 0, 330, 0 };
    ENV envClarFrq14[] = { 1, 0, 2, 0, 3, 4213, 20, 4119, 36, 1566, 48, 4344, 130, 4388, 228, 4388, 229, 0, 330, 0 };
    ENV envClarAmp15[] = { 1, 0, 5, 0, 23, 1, 28, 0, 50, 0, 77, 14, 202, 1, 219, 2, 247, 0, 330, 0 };
    ENV envClarFrq15[] = { 1, 0, 3, 0, 5, 4624, 28, 4496, 33, 1012, 48, 4649, 122, 4703, 247, 4685, 248, 0, 330, 0 };
    ENV envClarAmp16[] = { 1, 0, 14, 0, 24, 0, 38, 0, 64, 12, 104, 4, 145, 4, 215, 1, 238, 0, 330, 0 };
    ENV envClarFrq16[] = { 1, 4928, 24, 4751, 36, 1072, 52, 4965, 117, 5006, 155, 5003, 198, 5020, 238, 3197, 239, 0, 330, 0 };
    ENV envClarAmp17[] = { 1, 0, 58, 0, 95, 12, 136, 13, 201, 1, 220, 3, 233, 0, 330, 0 };
    ENV envClarFrq17[] = { 1, 0, 45, 0, 46, 5005, 58, 3759, 63, 5285, 119, 5325, 180, 5325, 233, 5367, 234, 0, 330, 0 };
    ENV envClarAmp18[] = { 1, 0, 50, 0, 61, 5, 100, 0, 141, 4, 185, 2, 208, 0, 330, 0 };
    ENV envClarFrq18[] = { 1, 0, 48, 0, 50, 4926, 52, 5563, 94, 5628, 113, 5602, 137, 5634, 208, 5646, 210, 0, 330, 0 };
    ENV envClarAmp19[] = { 1, 0, 58, 0, 63, 1, 85, 0, 140, 1, 171, 0, 183, 0, 330, 0 };
    ENV envClarFrq19[] = { 1, 0, 56, 0, 58, 3938, 65, 5753, 79, 5930, 104, 5889, 152, 5916, 183, 5880, 184, 0, 330, 0 };
    ENV envClarAmp20[] = { 1, 0, 50, 0, 64, 5, 103, 1, 139, 1, 177, 2, 219, 0, 330, 0 };
    ENV envClarFrq20[] = { 1, 0, 48, 0, 50, 5192, 58, 6209, 121, 6266, 190, 6266, 204, 6238, 219, 6288, 220, 0, 330, 0 };
    ENV envClarAmp21[] = { 1, 0, 70, 0, 79, 3, 113, 3, 141, 1, 206, 1, 219, 0, 330, 0 };
    ENV envClarFrq21[] = { 1, 0, 69, 0, 70, 4245, 77, 6537, 116, 6567, 140, 6571, 176, 6564, 219, 6583, 220, 0, 330, 0 };
    
    //以下定义小号的泛音集
    PRT prtTrum [12] =  { sizeof (envTrumAmp01) / sizeof (ENV), envTrumAmp01,
                          sizeof (envTrumFrq01) / sizeof (ENV), envTrumFrq01,
                          sizeof (envTrumAmp02) / sizeof (ENV), envTrumAmp02,
                          sizeof (envTrumFrq02) / sizeof (ENV), envTrumFrq02,
                          sizeof (envTrumAmp03) / sizeof (ENV), envTrumAmp03,
                          sizeof (envTrumFrq03) / sizeof (ENV), envTrumFrq03,
                          sizeof (envTrumAmp04) / sizeof (ENV), envTrumAmp04,
                          sizeof (envTrumFrq04) / sizeof (ENV), envTrumFrq04,
                          sizeof (envTrumAmp05) / sizeof (ENV), envTrumAmp05,
                          sizeof (envTrumFrq05) / sizeof (ENV), envTrumFrq05,
                          sizeof (envTrumAmp06) / sizeof (ENV), envTrumAmp06,
                          sizeof (envTrumFrq06) / sizeof (ENV), envTrumFrq06,
                          sizeof (envTrumAmp07) / sizeof (ENV), envTrumAmp07,
                          sizeof (envTrumFrq07) / sizeof (ENV), envTrumFrq07,
                          sizeof (envTrumAmp08) / sizeof (ENV), envTrumAmp08,
                          sizeof (envTrumFrq08) / sizeof (ENV), envTrumFrq08,
                          sizeof (envTrumAmp09) / sizeof (ENV), envTrumAmp09,
                          sizeof (envTrumFrq09) / sizeof (ENV), envTrumFrq09,
                          sizeof (envTrumAmp10) / sizeof (ENV), envTrumAmp10,
                          sizeof (envTrumFrq10) / sizeof (ENV), envTrumFrq10,
                          sizeof (envTrumAmp11) / sizeof (ENV), envTrumAmp11,
                          sizeof (envTrumFrq11) / sizeof (ENV), envTrumFrq11,
                          sizeof (envTrumAmp12) / sizeof (ENV), envTrumAmp12,
                          sizeof (envTrumFrq12) / sizeof (ENV), envTrumFrq12 } ;
    
    //以下定义双簧管的泛音集
    PRT prtOboe [21] =  { sizeof (envOboeAmp01) / sizeof (ENV), envOboeAmp01,
                          sizeof (envOboeFrq01) / sizeof (ENV), envOboeFrq01,
                          sizeof (envOboeAmp02) / sizeof (ENV), envOboeAmp02,
                          sizeof (envOboeFrq02) / sizeof (ENV), envOboeFrq02,
                          sizeof (envOboeAmp03) / sizeof (ENV), envOboeAmp03,
                          sizeof (envOboeFrq03) / sizeof (ENV), envOboeFrq03,
                          sizeof (envOboeAmp04) / sizeof (ENV), envOboeAmp04,
                          sizeof (envOboeFrq04) / sizeof (ENV), envOboeFrq04,
                          sizeof (envOboeAmp05) / sizeof (ENV), envOboeAmp05,
                          sizeof (envOboeFrq05) / sizeof (ENV), envOboeFrq05,
                          sizeof (envOboeAmp06) / sizeof (ENV), envOboeAmp06,
                          sizeof (envOboeFrq06) / sizeof (ENV), envOboeFrq06,
                          sizeof (envOboeAmp07) / sizeof (ENV), envOboeAmp07,
                          sizeof (envOboeFrq07) / sizeof (ENV), envOboeFrq07,
                          sizeof (envOboeAmp08) / sizeof (ENV), envOboeAmp08,
                          sizeof (envOboeFrq08) / sizeof (ENV), envOboeFrq08,
                          sizeof (envOboeAmp09) / sizeof (ENV), envOboeAmp09,
                          sizeof (envOboeFrq09) / sizeof (ENV), envOboeFrq09,
                          sizeof (envOboeAmp10) / sizeof (ENV), envOboeAmp10,
                          sizeof (envOboeFrq10) / sizeof (ENV), envOboeFrq10,
                          sizeof (envOboeAmp11) / sizeof (ENV), envOboeAmp11,
                          sizeof (envOboeFrq11) / sizeof (ENV), envOboeFrq11,
                          sizeof (envOboeAmp12) / sizeof (ENV), envOboeAmp12,
                          sizeof (envOboeFrq12) / sizeof (ENV), envOboeFrq12,
                          sizeof (envOboeAmp13) / sizeof (ENV), envOboeAmp13,
                          sizeof (envOboeFrq13) / sizeof (ENV), envOboeFrq13,
                          sizeof (envOboeAmp14) / sizeof (ENV), envOboeAmp14,
                          sizeof (envOboeFrq14) / sizeof (ENV), envOboeFrq14,
                          sizeof (envOboeAmp15) / sizeof (ENV), envOboeAmp15,
                          sizeof (envOboeFrq15) / sizeof (ENV), envOboeFrq15,
                          sizeof (envOboeAmp16) / sizeof (ENV), envOboeAmp16,
                          sizeof (envOboeFrq16) / sizeof (ENV), envOboeFrq16,
                          sizeof (envOboeAmp17) / sizeof (ENV), envOboeAmp17,
                          sizeof (envOboeFrq17) / sizeof (ENV), envOboeFrq17,
                          sizeof (envOboeAmp18) / sizeof (ENV), envOboeAmp18,
                          sizeof (envOboeFrq18) / sizeof (ENV), envOboeFrq18,
                          sizeof (envOboeAmp19) / sizeof (ENV), envOboeAmp19,
                          sizeof (envOboeFrq19) / sizeof (ENV), envOboeFrq19,
                          sizeof (envOboeAmp20) / sizeof (ENV), envOboeAmp20,
                          sizeof (envOboeFrq20) / sizeof (ENV), envOboeFrq20,
                          sizeof (envOboeAmp21) / sizeof (ENV), envOboeAmp21,
                          sizeof (envOboeFrq21) / sizeof (ENV), envOboeFrq21 } ;
    
    //以下定义单簧管的泛音集
    PRT prtClar [21] =  { sizeof (envClarAmp01) / sizeof (ENV), envClarAmp01,
                          sizeof (envClarFrq01) / sizeof (ENV), envClarFrq01,
                          sizeof (envClarAmp02) / sizeof (ENV), envClarAmp02,
                          sizeof (envClarFrq02) / sizeof (ENV), envClarFrq02,
                          sizeof (envClarAmp03) / sizeof (ENV), envClarAmp03,
                          sizeof (envClarFrq03) / sizeof (ENV), envClarFrq03,
                          sizeof (envClarAmp04) / sizeof (ENV), envClarAmp04,
                          sizeof (envClarFrq04) / sizeof (ENV), envClarFrq04,
                          sizeof (envClarAmp05) / sizeof (ENV), envClarAmp05,
                          sizeof (envClarFrq05) / sizeof (ENV), envClarFrq05,
                          sizeof (envClarAmp06) / sizeof (ENV), envClarAmp06,
                          sizeof (envClarFrq06) / sizeof (ENV), envClarFrq06,
                          sizeof (envClarAmp07) / sizeof (ENV), envClarAmp07,
                          sizeof (envClarFrq07) / sizeof (ENV), envClarFrq07,
                          sizeof (envClarAmp08) / sizeof (ENV), envClarAmp08,
                          sizeof (envClarFrq08) / sizeof (ENV), envClarFrq08,
                          sizeof (envClarAmp09) / sizeof (ENV), envClarAmp09,
                          sizeof (envClarFrq09) / sizeof (ENV), envClarFrq09,
                          sizeof (envClarAmp10) / sizeof (ENV), envClarAmp10,
                          sizeof (envClarFrq10) / sizeof (ENV), envClarFrq10,
                          sizeof (envClarAmp11) / sizeof (ENV), envClarAmp11,
                          sizeof (envClarFrq11) / sizeof (ENV), envClarFrq11,
                          sizeof (envClarAmp12) / sizeof (ENV), envClarAmp12,
                          sizeof (envClarFrq12) / sizeof (ENV), envClarFrq12,
                          sizeof (envClarAmp13) / sizeof (ENV), envClarAmp13,
                          sizeof (envClarFrq13) / sizeof (ENV), envClarFrq13,
                          sizeof (envClarAmp14) / sizeof (ENV), envClarAmp14,
                          sizeof (envClarFrq14) / sizeof (ENV), envClarFrq14,
                          sizeof (envClarAmp15) / sizeof (ENV), envClarAmp15,
                          sizeof (envClarFrq15) / sizeof (ENV), envClarFrq15,
                          sizeof (envClarAmp16) / sizeof (ENV), envClarAmp16,
                          sizeof (envClarFrq16) / sizeof (ENV), envClarFrq16,
                          sizeof (envClarAmp17) / sizeof (ENV), envClarAmp17,
                          sizeof (envClarFrq17) / sizeof (ENV), envClarFrq17,
                          sizeof (envClarAmp18) / sizeof (ENV), envClarAmp18,
                          sizeof (envClarFrq18) / sizeof (ENV), envClarFrq18,
                          sizeof (envClarAmp19) / sizeof (ENV), envClarAmp19,
                          sizeof (envClarFrq19) / sizeof (ENV), envClarFrq19,
                          sizeof (envClarAmp20) / sizeof (ENV), envClarAmp20,
                          sizeof (envClarFrq20) / sizeof (ENV), envClarFrq20,
                          sizeof (envClarAmp21) / sizeof (ENV), envClarAmp21,
                          sizeof (envClarFrq21) / sizeof (ENV), envClarFrq21 } ;
    
    INS insTrum = { 360, 12, prtTrum } ;//定义小号的乐器结构并赋值
    INS insOboe = { 313, 21, prtOboe } ;//定义双簧管的乐器结构并赋值
    INS insClar = { 330, 21, prtClar } ;//定义单簧管的乐器结构并赋值

    //resource.h

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

    //Addsynth.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
    //
    
    ADDSYNTH DIALOGEX 0, 0, 229, 66
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "加法合成器(Additive Synthesis)"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        PUSHBUTTON      "小号
    (Trumpet)",IDC_TRUMPET,19,14,55,24,BS_MULTILINE
        PUSHBUTTON      "双簧管
    (Oboe)",IDC_OBOE,89,14,50,24,BS_MULTILINE
        PUSHBUTTON      "单簧管
    (Clarinet)",IDC_CLARINET,159,15,50,24,BS_MULTILINE
        LTEXT           "数据准备中(Preparing Data)...",IDC_TEXT,19,44,192,8
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        "ADDSYNTH", DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 222
            TOPMARGIN, 7
            BOTTOMMARGIN, 59
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED

    22.2.9 波形音频闹钟

    (1)通用控件库:

      ①#include <commctrl.h>和#pragma comment(lib,"Comctl32.lib")

      ②初始化InitCommonControlsEx(&icex);//通用控件的数量多,所在默认情况下Comctl32.dll并不载入内存,需用专用函数来完成加载。使用InitCommonControls载入库时,会注册所有通用控件类,而InitCommonControlsEx会载入扩展通用控件

    (2)利用FILETIME结构进行时间的加减

         //为了避免SYSTEMTIME中加9小时后可能出现的日期或年份进位,将系统时间转为文件时间,加9小时后,再转化为系统时间,最后将此时间设置控件的时间
    
        GetLocalTime(&st);  //GetLocalTime能够得到本地电脑设置时区的时间
    
        SystemTimeToFileTime(&st, &ft); //将系统时间转为文件时间
    
        li = *(LARGE_INTEGER*)&ft;      //将时间存入一个大整数结构体
    
        li.QuadPart += 9 * FTTICKSPERHOUR;//加上9个小时包含的100纳秒的数量
    
                                          //Quad表示4字,即64位。
    
        ft = *(FILETIME*)&li;   //将大整数结构体转换为文件时间
    
        FileTimeToSystemTime(&ft, &st);
    
        st.wMinute = st.wSecond = st.wMilliseconds = 0;

    【WakeUp程序】

    /*------------------------------------------------------------
       WAKEUP.C -- Alarm Clock Program
                     (c) Charles Petzold, 1998
      ------------------------------------------------------------*/
    
    #include <windows.h>
    #include <commctrl.h> //包含通用控件头文件
    
    #pragma comment(lib,"Comctl32.lib")
    #pragma comment(lib,"WINMM.lib")
    
     
    //三个子窗口ID
    #define ID_TIMEPICK    0  //时间控件ID
    #define ID_CHECKBOX    1  //复选框按钮ID
    #define ID_PUSHBTN     2  //关闭响铃按钮ID
    
    //计时器ID
    #define ID_TIMER       1
    
    //定义1小时包含多少个100纳秒(nanosecond:十亿分之一秒)的数量
    #define FTTICKSPERHOUR  (60*60*(LONGLONG)10000000)
    
    //定义音频流结构文件
    #define        SAMPRATE      11025         //采样率
    #define        NUMSAMPS      (3*SAMPRATE)  //采样的数量
    #define        HALFSAMPS     (NUMSAMPS /2) //采样数量的一半 
    
    //定义WAV波形文件结构
    typedef struct
    {
        char chRiff[4];
        DWORD dwRiffSize;
        char chWave[4];
        char chFmt[4];
        DWORD dwFmtSize;
        PCMWAVEFORMAT  pwf;
        char chData[4];
        DWORD dwDataSize;
        BYTE  byData[0];
    }WAVEFORM,*PWAVEFORM;
    
    //主窗口过程与子窗口类过程
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    LRESULT CALLBACK SubProc(HWND, UINT, WPARAM, LPARAM);
    
    //存储子控件旧的窗口过程
    WNDPROC  SubbedProc[3];
    
    //当前具有输入焦点的子窗口句柄
    HWND hwndFocus;
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("WakeUp") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         =0;  // CS_HREDRAW | CS_VREDRAW;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) (1+ COLOR_BTNFACE) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {
              MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
         
         hwnd = CreateWindow (szAppName,                  // window class name
                              szAppName, // window caption
                              WS_OVERLAPPED | WS_CAPTION |
                                              WS_SYSMENU | WS_MINIMIZEBOX,        // window style
                              CW_USEDEFAULT,              // initial x position
                              CW_USEDEFAULT,              // initial y position
                              CW_USEDEFAULT,              // initial x size
                              CW_USEDEFAULT,              // initial y size
                              NULL,                       // parent window handle
                              NULL,                       // window menu handle
                              hInstance,                  // program instance handle
                              NULL) ;                     // creation parameters
         
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
         
         while (GetMessage (&msg, NULL, 0, 0))
         {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
         }
         return msg.wParam ;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static HWND hwndDTP, hwndCheck, hwndPush; //时间控件、复选框、按钮
         INITCOMMONCONTROLSEX icex; //须包含commctrl.h文件头
    
         HINSTANCE hInstance;
         int cxChar, cyChar;
         SYSTEMTIME   st;//系统时间变量
         FILETIME     ft;//文件时间变量,FILETIME结构持有的64位无符号的文件的日期和时间值。
                          //此值表示自1601年1月1日开始的100纳秒为单位的时间
    
         LARGE_INTEGER        li;   //64位,用于储存时间的大整数结构体变量
    
         static WAVEFORM waveform = { "RIFF", NUMSAMPS + 0x24, "WAVE", "fmt ", 
                                     sizeof(PCMWAVEFORMAT),1,1,SAMPRATE,SAMPRATE,1,8,"data",
                                     NUMSAMPS};
         static WAVEFORM *pwaveform;  //定义音频结构体变量
         int i;
    
         switch (message)
         {
         case WM_CREATE:
              hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
              cxChar = LOWORD(GetDialogBaseUnits());
              cyChar = HIWORD(GetDialogBaseUnits());
    
              //SWP_NOMOVE:维持当前位置(忽略X和Y参数)
              //SWP_NOACTIVATE:不激活窗口。如果未设置标志,则窗口被激活,
              //并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)
              //SWP_NOZORDER:维持当前Z序(忽略hWndlnsertAfter参数)
              SetWindowPos(hwnd, NULL, 0, 0,
                           42*cxChar,
                           10 * cyChar / 3 + 2 * GetSystemMetrics(SM_CYBORDER)+
                                                 GetSystemMetrics(SM_CYCAPTION),
                           SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    
              icex.dwSize = sizeof(icex);
              icex.dwICC = ICC_DATE_CLASSES;
    
               //通用控件的数量多,所在默认情况下Comctl32.dll并不载入内存,需用专用函数来完成加载
               //使用InitCommonControls载入库时,会注册所有通用控件类,而InitCommonControlsEx会载
               //入扩展通用控件
              InitCommonControlsEx(&icex);
    
              //用方波数据交替创建波形文件
              pwaveform = malloc(sizeof(WAVEFORM)+NUMSAMPS); //WAVEFORM为自定义的WAV音频结构体
              *pwaveform = waveform;  //填入WAV文件前面的数据
              for (i = 0; i < HALFSAMPS; i++)  //0-HALFSAMPS-1:有波形数据。HALFSAMEPLES以后静音
              {
                  if (i % 600 <300)
                  {
                      if (i % 16 < 8)
                          pwaveform->byData[i] = 25;
                      else
                          pwaveform->byData[i] = 230;
             
                  }
                  else
                  {
                      if (i % 8 < 4)
                          pwaveform->byData[25];
                      else
                          pwaveform->byData[230];
                  }
              }
    
              //创建三个子控件
              hwndDTP = CreateWindow(DATETIMEPICK_CLASS, TEXT(""), 
                                     WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_TIMEFORMAT,
                                     2*cxChar,cyChar*2/3,12*cxChar,4*cyChar /3,
                                     hwnd,(HMENU)ID_TIMEPICK,hInstance,NULL);
    
              hwndCheck = CreateWindow(TEXT("Button"), TEXT("Set Alarm"), 
                                       WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
                                       16*cxChar,cyChar*2/3,12*cxChar,4*cyChar/3,
                                       hwnd,(HMENU)ID_CHECKBOX,hInstance,NULL);
    
              hwndPush = CreateWindow(TEXT("Button"), TEXT("Turn Off"),
                                     WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
                                     28 * cxChar, cyChar * 2 / 3, 11 * cxChar, 4 * cyChar / 3,
                                     hwnd, (HMENU)ID_PUSHBTN, hInstance, NULL);
              
              hwndFocus = hwndDTP;
    
              //子窗口类化三个子窗口控件
              SubbedProc[ID_TIMEPICK] = (WNDPROC)SetWindowLong(hwndDTP, GWL_WNDPROC, (LONG)SubProc);
              SubbedProc[ID_CHECKBOX] = (WNDPROC)SetWindowLong(hwndCheck, GWL_WNDPROC, (LONG)SubProc);
              SubbedProc[ID_PUSHBTN]  = (WNDPROC)SetWindowLong(hwndPush, GWL_WNDPROC, (LONG)SubProc);
    
              //将时间控件设置为当前计算机的时间加9个小时,表示9小时以后闹钟响铃
              //为了避免st加9小时后可能出现的日期或年份进位,将系统时间转为文件时间,加9小时后,再
              //转化为系统时间,最后将此时间设置控件的时间
              GetLocalTime(&st);  //GetLocalTime能够得到本地电脑设置时区的时间
              SystemTimeToFileTime(&st, &ft); //将系统时间转为文件时间
              li = *(LARGE_INTEGER*)&ft;      //将时间存入一个大整数结构体
              li.QuadPart += 9 * FTTICKSPERHOUR;//加上9个小时包含的100纳秒的数量
              ft = *(FILETIME*)&li;   //将大整数结构体转换为文件时间
              FileTimeToSystemTime(&ft, &st);
              st.wMinute = st.wSecond = st.wMilliseconds = 0;
    
              SendMessage(hwndDTP, DTM_SETSYSTEMTIME, 0, (LPARAM)&st);//定时,设置响铃时间
              return 0 ;
    
         case WM_SETFOCUS:
             SetFocus(hwndFocus);
             return 0;
    
         case WM_COMMAND:
             switch (LOWORD(wParam))
             {
             case ID_CHECKBOX:  //处理复选框按钮
                 //当按下“Set Alarm”时,取得控件当前的时间并减去当前系统时间
                 if (SendMessage(hwndCheck,BM_GETCHECK,0,0))
                 {
                     //获得控件时间
                     SendMessage(hwndDTP, DTM_GETSYSTEMTIME, 0, (LPARAM)&st);
                     SystemTimeToFileTime(&st, &ft);
                     li = *(LARGE_INTEGER*)&ft;
    
                     //获取当前计算机时间
                     GetLocalTime(&st);
                     SystemTimeToFileTime(&st, &ft);
                     li.QuadPart -= ((LARGE_INTEGER*)&ft)->QuadPart; //计算时间间隔
    
                     //确保时差在0-24小时范围内
                     while (li.QuadPart < 0)
                         li.QuadPart += 24 * FTTICKSPERHOUR; //确保时差>=0;
    
                     li.QuadPart %= 24 * FTTICKSPERHOUR;    //确保时差<24;
    
                     //设置一个一次性的计时器,触发时间为以上的计算所得的时差
                     //x(ms)= (li.QuadPart*100(ns)/1000000000)*1000 =li.QuadPart/10000
                     SetTimer(hwnd, ID_TIMER, (int)(li.QuadPart / 10000), 0);
    
                 }
                 //如果未选中复选框,则删除计时器
                 else
                     KillTimer(hwnd, ID_TIMER);
    
                 return 0;
    
             case ID_PUSHBTN:
                 PlaySound(NULL, NULL, 0); //播放声间
                 SendMessage(hwndCheck, BM_SETCHECK, 0, 0); //取消复选框选中状态
                 EnableWindow(hwndDTP, TRUE);
                 EnableWindow(hwndCheck, TRUE);
                 EnableWindow(hwndPush, FALSE);
                 SetFocus(hwndDTP);
                 return 0;
             }
             break;
    
         case WM_NOTIFY: //来自时间控件的通知消息
             //当用户选中复选框,然后再去改变时间控件的值时,会导致显示的时间和响铃的时间不同,
             //因此,在此种情况下改变时间控件时,则删除计时器,并让取消复选框选中状态,让用户
             //重新设置。
             switch (wParam) //wParam:控件ID。lParam——指向NMHDR结构(包含通知码和其他额外信息)
             {               //NMHDR:含三个字段:hwndFrom、idFrom、code
             case ID_TIMEPICK:
                 switch (((NMHDR*)lParam)->code)
                 {
                 case DTN_DATETIMECHANGE:  //改变控件时间通知
                     if (SendMessage(hwndCheck,BM_GETCHECK,0,0))
                     {
                         KillTimer(hwnd, ID_TIMER);
                         SendMessage(hwndCheck, BM_SETCHECK, 0, 0);
                     }
                     return 0;
                 }
             }
             return 0;
    
         case WM_TIMER://当响铃时间到来时,收到此消息
             KillTimer(hwnd, ID_TIMER); 
             PlaySound((PTSTR)pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC); //异步循环播放
    
             EnableWindow(hwndDTP, FALSE);
             EnableWindow(hwndCheck, FALSE);
             EnableWindow(hwndPush, TRUE);
             hwndFocus = hwndPush; //这里不需要调用SetFocus来设置按钮为焦点窗口,因为后面的ShowWindow会向对话框恢复,
                                   //就会调用本窗口过程WM_SETFOCUS去处理。
             ShowWindow(hwnd, SW_RESTORE); //如果窗口最小化,则恢复主窗口。
             SetForegroundWindow(hwnd); //将主窗口设置到前台,并激活,以便接收键盘消息(因为用户要用键盘取消响铃)
                                        
             return 0;
    
         case WM_DESTROY:
              if (pwaveform)
                 free(pwaveform);
    
              if (IsWindowEnabled(hwndPush))
                  PlaySound(NULL, NULL, 0); //停止响铃
    
              if (SendMessage(hwnd, BM_GETCHECK, 0, 0))
                  KillTimer(hwnd, ID_TIMER);
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    
    LRESULT CALLBACK  SubProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        int id,idNext;
        id = GetWindowLong(hwnd, GWL_ID);  //获取子窗口控件ID
        switch (message)
        {
        case WM_CHAR:
                if (wParam == '	') //处理按下Tab键消息
            {
                idNext = id;
                do
                {
                    //如果同时按下Shift时,跳两个控件
                    idNext = (idNext + (GetKeyState(VK_SHIFT) < 0 ? 2 : 1)) % 3;
                } while (!IsWindowEnabled(GetDlgItem(GetParent(hwnd),idNext)));
                SetFocus(GetDlgItem(GetParent(hwnd), idNext));
                return 0;
            }
            break;
    
        case WM_SETFOCUS:
            hwndFocus = hwnd;
            break;
        }
        //传给子控件旧的窗口过程处理
        return CallWindowProc(SubbedProc[id], hwnd, message, wParam, lParam);
    
    }

    22.3 MIDI和音乐(本节未读过)

  • 相关阅读:
    深入理解JavaScript定时器(续)
    也谈前端基础设施建设
    Reporting Services在指定计算机上找不到报表服务器
    优化tempdb提高SQL Server的性能
    SQL 代理服务未运行。此操作需要 SQL 代理服务。 (rsSchedulerNotResponding) 获取联机帮助
    报表服务器上出现内部错误。有关详细信息,请参阅错误日志。 (rsInternalError) 获取联机帮助.找不到存储过程 'GetOneConfigurationInfo'。
    表中包含有外键时无法进行导入数据,
    SQLSTATE ODBC API(驱动程序管理器)错误
    数据库只能用机器名连接,不能用ip地址连接
    请教:不能访问通过IP访问,却可以通过机器名访问
  • 原文地址:https://www.cnblogs.com/5iedu/p/4715233.html
Copyright © 2011-2022 走看看