c语言高级主题 作者Charles Petzold, 1998年编写的各种关于乐器,通过程序合成实现同样声音效果 我只更改文字显示部分,对于初学者以及部分计算机学生有很好的参考价值。

//作者Charles Petzold是Windows编程界一位大师,世界顶级技术作家,即使过去这么多年

//其程序现在仍然完美运行于Windows之上。我的是Windows10版本测试运行通过。所以

//其源代码代码全部集中在一个文件,作者命名规范,注释清晰,参考价值非常高。

/*---------------------------------------
   KBMIDI.C -- Keyboard MIDI Player 
               (c) Charles Petzold, 1998
  ---------------------------------------*/

#include

// Defines for Menu IDs
// --------------------

#define IDM_OPEN    0x100
#define IDM_CLOSE   0x101
#define IDM_DEVICE  0x200
#define IDM_CHANNEL 0x300
#define IDM_VOICE   0x400

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

TCHAR    szAppName [] = TEXT ("KBMidi") ;
HMIDIOUT hMidiOut ;
int      iDevice = MIDIMAPPER, iChannel = 0, iVoice = 0, iVelocity = 64 ;
int      cxCaps, cyChar, xOffset, yOffset ;

     // Structures and data for showing families and instruments on menu
     // ----------------------------------------------------------------

typedef struct
{
     TCHAR * szInst ;
     int    iVoice ;
}
INSTRUMENT ;

typedef struct
{
     TCHAR      * szFam ;
     INSTRUMENT   inst [8] ;
}
FAMILY ;

FAMILY fam [16] = { 
     
     TEXT ("钢琴Piano"),

          TEXT ("大钢琴"),                      0,//
          TEXT ("明亮钢琴"),                    1,//
          TEXT ("电子钢琴"),                    2,
          TEXT ("酒吧钢琴"),                    3,//Honky-tonk Piano
          TEXT ("信号音电钢琴"),                4,//Rhodes Piano
          TEXT ("合唱钢琴"),                    5,     //合唱钢琴Chorused
          TEXT ("羽管键琴"),                    6,//Harpsichord
          TEXT ("电子击弦古钢琴"),              7,//电子击弦古钢琴Clavinet

     TEXT ("半音阶打击乐器"),

          TEXT ("钢片琴"),                      8,
          TEXT ("钟琴"),                        9,
          TEXT ("八音盒"),                      10,
          TEXT ("电颤琴"),                      11,
          TEXT ("马林巴"),                      12,
          TEXT ("木琴"),                        13,
          TEXT ("管状铃铛"),                    14,
          TEXT ("扬琴"),                        15,

     TEXT ("风琴"),

          TEXT ("哈蒙德风琴"),                  16,
          TEXT ("冲击琴"),                      17,
          TEXT ("摇滚管风琴"),                  18,
          TEXT ("教堂管风琴"),                  19,
          TEXT ("簧风琴"),                      20,
          TEXT ("手风琴"),                      21,
          TEXT ("口琴"),                        22,
          TEXT ("探戈式手风琴"),                23,

     TEXT ("吉他"),

          TEXT ("尼龙弦吉他"),                  24,
          TEXT ("钢弦吉他"),                    25,
          TEXT ("爵士电吉他"),                  26,
          TEXT ("清音电吉他"),                  27,
          TEXT ("闷音电吉他"),                  28,
          TEXT ("加驱动效果的电吉他"),          29,
          TEXT ("加失真效果的电吉他"),          30,
          TEXT ("吉他和音"),                    31,

     TEXT ("贝司"),

          TEXT (" 大贝司"),                     32,
          TEXT ("电贝司(指弹"),                33,
          TEXT (" 电贝司(拨片)"),             34,
          TEXT ("无品贝司"),                    35,
          TEXT (" 掌击贝斯 1"),                 36,
          TEXT ("掌击贝斯2"),                   37,
          TEXT ("电子合成 1"),                  38,
          TEXT ("电子合成 2"),                  39,

     TEXT ("弦乐"),

          TEXT ("古典小提琴"),                  40,
          TEXT ("中提琴"),                      41,
          TEXT ("大提琴"),                      42,
          TEXT ("低音大提琴"),                  43,
          TEXT ("弦乐群颤音音色"),              44,
          TEXT ("弦乐群拨弦音色"),              45,
          TEXT ("竖琴"),                        46,
          TEXT ("定音鼓"),                      47,

     TEXT ("合奏合唱"),

          TEXT ("弦乐合奏音色 1"),              48,
          TEXT ("弦乐合奏音色 2"),              49,
          TEXT ("合成弦乐合奏音色 1"),          50,
          TEXT ("合成弦乐合奏音色 2"),          51,
          TEXT ("啊唱音"),                      52,
          TEXT ("人声嘟"),                      53,
          TEXT ("合成人声"),                    54,
          TEXT ("管弦乐敲击齐奏"),             55,

     TEXT ("铜管乐器"),

          TEXT ("小号"),                        56,
          TEXT (" 长号"),                       57,
          TEXT ("大号"),                        58,
          TEXT ("加弱音器小号"),                59,
          TEXT ("法国号圆号"),                  60,
          TEXT ("铜管乐器合奏"),                61,
          TEXT ("合成铜管音色 1"),              62,
          TEXT ("合成铜管音色2"),               63,

     TEXT ("簧管"),

          TEXT ("高音萨克斯风"),                64,
          TEXT ("次中音萨克斯风"),              65,
          TEXT ("中音萨克斯风"),                66,
          TEXT ("低音萨克斯风"),                67,
          TEXT ("双簧管"),                      68,
          TEXT ("英国管"),                      69,
          TEXT ("巴松大管"),                    70,
          TEXT ("单簧管黑管"),                  71,

     TEXT ("笛子"),

          TEXT ("短笛"),                        72,
          TEXT ("长笛"),                        73,
          TEXT ("竖笛"),                        74,
          TEXT ("排箫"),                        75,
          TEXT ("吹瓶口声笛"),                  76,
          TEXT ("日本尺八"),                    77,
          TEXT ("口哨声"),                      78,
          TEXT ("奥卡雷那"),                    79,

     TEXT ("合成器主音"),

          TEXT ("主音 1 (广场式)"),             80,
          TEXT ("主音 2 (锯齿式)"),             81,
          TEXT ("主音 3 (卡利奥普铅)"),         82,
          TEXT ("主音 4 (chiff lead)"),         83,
          TEXT ("主音 5 (查郎)"),               84,
          TEXT ("主音 6 (声音式)"),             85,
          TEXT ("主音 7 (五分之一式)"),         86,
          TEXT ("主音 8 (管式 + lead)"),        87,

     TEXT ("合成式背景音"),

          TEXT ("背景音 1 (新世纪)"),           88,
          TEXT ("背景音 2 (温暖式)"),           89,
          TEXT ("背景音 3 (合成式)"),           90,
          TEXT ("背景音 4 (吟诗式)"),           91,
          TEXT ("背景音 5 (琴式)"),             92,
          TEXT ("背景音 6 (金属式)"),           93,
          TEXT ("背景音7 (环绕式)"),            94,
          TEXT ("背景音 8 (快速式)"),           95,

     TEXT ("合成效果"),

          TEXT ("FX 1 (下雨式)"),               96,
          TEXT ("FX 2 (原声带)"),               97,
          TEXT ("FX 3 (晶体式)"),               98,
          TEXT ("FX 4 (氛围式)"),               99,
          TEXT ("FX 5 (明亮式)"),               100,
          TEXT ("FX 6 (哥布林)"),               101,
          TEXT ("FX 7 (回荡式)"),               102,
          TEXT ("FX 8 (sci-fi)"),               103,

     TEXT ("民族式"),

          TEXT ("印度西塔琴"),                  104,
          TEXT ("非洲班卓琴"),                  105,
          TEXT ("日本三弦琴"),                  106,
          TEXT ("日本十三弦古筝"),              107,
          TEXT ("卡林巴琴"),                    108,
          TEXT ("欧洲风笛"),                    109,
          TEXT ("爱尔兰小提琴"),                110,
          TEXT ("日本山奈琴"),                  111,

     TEXT ("打击乐器"),

          TEXT ("叮当铃"),                      112,
          TEXT ("泰式桑巴舞乐器"),              113,
          TEXT ("钢鼓"),                        114,
          TEXT ("木鱼"),                        115,
          TEXT ("日本泰柯鼓"),                  116,
          TEXT ("通通鼓"),                      117,
          TEXT ("合成嗵鼓"),                    118,
          TEXT ("铜钹"),                        119,

     TEXT ("声音特效"),

          TEXT ("吉他滑弦杂音"),                120,
          TEXT ("呼吸杂音"),                    121,
          TEXT ("海岸声效"),                    122,
          TEXT ("鸟叫声效"),                    123,
          TEXT ("电话铃声效果"),                124,
          TEXT ("直升机效果"),                  125,
          TEXT ("拍掌声效"),                    126,
          TEXT ("枪声"),                        127 } ;

     // Data for translating scan codes to octaves and notes
     // ----------------------------------------------------

#define NUMSCANS    (sizeof key / sizeof key[0])

struct
{
     int     iOctave ;
     int     iNote ;
     int     yPos ;
     int     xPos ;
     TCHAR * szKey ;
}
key [] =
{
                                         // Scan  Char  Oct  Note
                                         // ----  ----  ---  ----
         -1, -1, -1, -1, NULL,           //   0   None
         -1, -1, -1, -1, NULL,           //   1   Esc
         -1, -1,  0,  0, TEXT (""),      //   2     1
          5,  1,  0,  2, TEXT ("C#"),    //   3     2    5    C#
          5,  3,  0,  4, TEXT ("D#"),    //   4     3    5    D#
         -1, -1,  0,  6, TEXT (""),      //   5     4
          5,  6,  0,  8, TEXT ("F#"),    //   6     5    5    F#
          5,  8,  0, 10, TEXT ("G#"),    //   7     6    5    G#
          5, 10,  0, 12, TEXT ("A#"),    //   8     7    5    A#
         -1, -1,  0, 14, TEXT (""),      //   9     8
          6,  1,  0, 16, TEXT ("C#"),    //  10     9    6    C#
          6,  3,  0, 18, TEXT ("D#"),    //  11     0    6    D#
         -1, -1,  0, 20, TEXT (""),      //  12     -
          6,  6,  0, 22, TEXT ("F#"),    //  13     =    6    F#
         -1, -1, -1, -1, NULL,           //  14    Back
          
         -1, -1, -1, -1, NULL,           //  15    Tab
          5,  0,  1,  1, TEXT ("C"),     //  16     q    5    C
          5,  2,  1,  3, TEXT ("D"),     //  17     w    5    D
          5,  4,  1,  5, TEXT ("E"),     //  18     e    5    E
          5,  5,  1,  7, TEXT ("F"),     //  19     r    5    F
          5,  7,  1,  9, TEXT ("G"),     //  20     t    5    G
          5,  9,  1, 11, TEXT ("A"),     //  21     y    5    A
          5, 11,  1, 13, TEXT ("B"),     //  22     u    5    B
          6,  0,  1, 15, TEXT ("C"),     //  23     i    6    C
          6,  2,  1, 17, TEXT ("D"),     //  24     o    6    D
          6,  4,  1, 19, TEXT ("E"),     //  25     p    6    E
          6,  5,  1, 21, TEXT ("F"),     //  26     [    6    F
          6,  7,  1, 23, TEXT ("G"),     //  27     ]    6    G
         -1, -1, -1, -1, NULL,           //  28    Ent
          
         -1, -1, -1, -1, NULL,           //  29    Ctrl
          3,  8,  2,  2, TEXT ("G#"),    //  30     a    3    G#
          3, 10,  2,  4, TEXT ("A#"),    //  31     s    3    A#
         -1, -1,  2,  6, TEXT (""),      //  32     d
          4,  1,  2,  8, TEXT ("C#"),    //  33     f    4    C#
          4,  3,  2, 10, TEXT ("D#"),    //  34     g    4    D#
         -1, -1,  2, 12, TEXT (""),      //  35     h
          4,  6,  2, 14, TEXT ("F#"),    //  36     j    4    F#
          4,  8,  2, 16, TEXT ("G#"),    //  37     k    4    G#
          4, 10,  2, 18, TEXT ("A#"),    //  38     l    4    A#
         -1, -1,  2, 20, TEXT (""),      //  39     ;
          5,  1,  2, 22, TEXT ("C#"),    //  40     '    5    C#
         -1, -1, -1, -1, NULL,           //  41     `
          
         -1, -1, -1, -1, NULL,           //  42    Shift
         -1, -1, -1, -1, NULL,           //  43     \  (not line continuation)
          3,  9,  3,  3, TEXT ("A"),     //  44     z    3    A
          3, 11,  3,  5, TEXT ("B"),     //  45     x    3    B
          4,  0,  3,  7, TEXT ("C"),     //  46     c    4    C
          4,  2,  3,  9, TEXT ("D"),     //  47     v    4    D
          4,  4,  3, 11, TEXT ("E"),     //  48     b    4    E
          4,  5,  3, 13, TEXT ("F"),     //  49     n    4    F
          4,  7,  3, 15, TEXT ("G"),     //  50     m    4    G
          4,  9,  3, 17, TEXT ("A"),     //  51     ,    4    A
          4, 11,  3, 19, TEXT ("B"),     //  52     .    4    B
          5,  0,  3, 21, TEXT ("C")      //  53     /    5    C
} ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     MSG      msg;
     HWND     hwnd ;
     WNDCLASS wndclass ;
     
     wndclass.style         = 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 = GetStockObject (WHITE_BRUSH) ;
     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, TEXT ("键盘式合成 播放器"),
                          WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     if (!hwnd)
          return 0 ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd); 
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

// Create the program's menu (called from WndProc, WM_CREATE)
// ----------------------------------------------------------

HMENU CreateTheMenu (int iNumDevs)
{
     TCHAR       szBuffer [32] ;
     HMENU       hMenu, hMenuPopup, hMenuSubPopup ;
     int         i, iFam, iIns ;
     MIDIOUTCAPS moc ;
     
     hMenu = CreateMenu () ;
     
          // Create "On/Off" popup menu
     
     hMenuPopup = CreateMenu () ;
     
     AppendMenu (hMenuPopup, MF_STRING, IDM_OPEN, TEXT ("&Open打开")) ; 
     AppendMenu (hMenuPopup, MF_STRING | MF_CHECKED, IDM_CLOSE, 
                             TEXT ("&Closed关闭")) ;
     
     AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, 
                        TEXT ("&Status状态")) ;
     
          // Create "Device" popup menu
     
     hMenuPopup = CreateMenu () ;
     
          // Put MIDI Mapper on menu if it's installed
     
     if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))
          AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + (int) MIDIMAPPER,
                         moc.szPname) ;
     else
          iDevice = 0 ;
     
          // Add the rest of the MIDI devices
     
     for (i = 0 ; i < iNumDevs ; i++)
     {
          midiOutGetDevCaps (i, &moc, sizeof (moc)) ;
          AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + i, moc.szPname) ;
     }
     
     CheckMenuItem (hMenuPopup, 0, MF_BYPOSITION | MF_CHECKED) ;
     AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, 
                        TEXT ("&Device设备")) ;
     
          // Create "Channel" popup menu
     
     hMenuPopup = CreateMenu () ;
     
     for (i = 0 ; i < 16 ; i++)
     {
          wsprintf (szBuffer, TEXT ("%d"), i + 1) ;
          AppendMenu (hMenuPopup, MF_STRING | (i ? MF_UNCHECKED : MF_CHECKED),
                                  IDM_CHANNEL + i, szBuffer) ;
     }
     
     AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, 
                        TEXT ("&Channel通道")) ;
     
          // Create "Voice" popup menu
     
     hMenuPopup = CreateMenu () ;
     
     for (iFam = 0 ; iFam < 16 ; iFam++)
     {
          hMenuSubPopup = CreateMenu () ;
          
          for (iIns = 0 ; iIns < 8 ; iIns++)
          {
               wsprintf (szBuffer, TEXT ("&%d.\t%s"), iIns + 1,
                                   fam[iFam].inst[iIns].szInst) ;
               AppendMenu (hMenuSubPopup,
                           MF_STRING | (fam[iFam].inst[iIns].iVoice ?
                                             MF_UNCHECKED : MF_CHECKED),
                           fam[iFam].inst[iIns].iVoice + IDM_VOICE,
                           szBuffer) ;
          }
          
          wsprintf (szBuffer, TEXT ("&%c.\t%s"), 'A' + iFam,
                              fam[iFam].szFam) ;
          AppendMenu (hMenuPopup, MF_STRING | MF_POPUP, (UINT) hMenuSubPopup,
                                  szBuffer) ;
     }
     AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup, 
                        TEXT ("&Voice声效")) ;
     return hMenu ;
}

// Routines for simplifying MIDI output
// ------------------------------------

DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel,
                      int iData1,  int iData2)
{
     DWORD dwMessage ;
     
     dwMessage = iStatus | iChannel | (iData1 << 8) | (iData2 << 16) ;
     
     return midiOutShortMsg (hMidi, dwMessage) ;
}

DWORD MidiNoteOff (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
{
     return MidiOutMessage (hMidi, 0x080, iChannel, 12 * iOct + iNote, iVel) ;
}

DWORD MidiNoteOn (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
{
     return MidiOutMessage (hMidi, 0x090, iChannel, 12 * iOct + iNote, iVel) ;
}

DWORD MidiSetPatch (HMIDIOUT hMidi, int iChannel, int iVoice)
{
     return MidiOutMessage (hMidi, 0x0C0, iChannel, iVoice, 0) ;
}

DWORD MidiPitchBend (HMIDIOUT hMidi, int iChannel, int iBend)
{
     return MidiOutMessage (hMidi, 0x0E0, iChannel, iBend & 0x7F, iBend >> 7) ;
}

// Draw a single key on window
// ---------------------------

VOID DrawKey (HDC hdc, int iScanCode, BOOL fInvert)
{
     RECT rc ;
     
     rc.left   = 3 * cxCaps * key[iScanCode].xPos / 2 + xOffset ;
     rc.top    = 3 * cyChar * key[iScanCode].yPos / 2 + yOffset ;
     rc.right  = rc.left + 3 * cxCaps ;
     rc.bottom = rc.top  + 3 * cyChar / 2 ;
     
     SetTextColor (hdc, fInvert ? 0x00FFFFFFul : 0x00000000ul) ;
     SetBkColor   (hdc, fInvert ? 0x00000000ul : 0x00FFFFFFul) ;
     
     FillRect (hdc, &rc, GetStockObject (fInvert ? BLACK_BRUSH : WHITE_BRUSH)) ;
     
     DrawText (hdc, key[iScanCode].szKey, -1, &rc,
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
     
     FrameRect (hdc, &rc, GetStockObject (BLACK_BRUSH)) ;
}

// Process a Key Up or Key Down message
// ------------------------------------

VOID ProcessKey (HDC hdc, UINT message, LPARAM lParam)
{
     int iScanCode, iOctave, iNote ;
     
     iScanCode = 0x0FF & HIWORD (lParam) ;
     
     if (iScanCode >= NUMSCANS)                       // No scan codes over 53
          return ;
     
     if ((iOctave = key[iScanCode].iOctave) == -1)    // Non-music key
          return ;
     
     if (GetKeyState (VK_SHIFT) < 0)
          iOctave += 0x20000000 & lParam ? 2 : 1 ;
     
     if (GetKeyState (VK_CONTROL) < 0)
          iOctave -= 0x20000000 & lParam ? 2 : 1 ;
     
     iNote = key[iScanCode].iNote ;
     
     if (message == WM_KEYUP)                           // For key up
     {
          MidiNoteOff (hMidiOut, iChannel, iOctave, iNote, 0) ;   // Note off
          DrawKey (hdc, iScanCode, FALSE) ;
          return ;
     }
     
     if (0x40000000 & lParam)                          // ignore typematics
          return ;
     
     MidiNoteOn (hMidiOut, iChannel, iOctave, iNote, iVelocity) ; // Note on
     DrawKey (hdc, iScanCode, TRUE) ;                 // Draw the inverted key
}

// Window Procedure
// ----------------

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static BOOL bOpened = FALSE ;
     HDC         hdc ;
     HMENU       hMenu ;
     int         i, iNumDevs, iPitchBend, cxClient, cyClient ;
     MIDIOUTCAPS moc ;
     PAINTSTRUCT ps ;
     SIZE        size ;
     TCHAR       szBuffer [16] ;
     
     switch (message)
     {
     case WM_CREATE:
               // Get size of capital letters in system font
          
          hdc = GetDC (hwnd) ;
          
          GetTextExtentPoint (hdc, TEXT ("M"), 1, &size) ;
          cxCaps = size.cx ;
          cyChar = size.cy ;
          
          ReleaseDC (hwnd, hdc) ;
          
               // Initialize "Volume" scroll bar
          
          SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ;
          SetScrollPos   (hwnd, SB_HORZ, iVelocity, TRUE) ;
          
               // Initialize "Pitch Bend" scroll bar
          
          SetScrollRange (hwnd, SB_VERT, 0, 16383, FALSE) ;
          SetScrollPos   (hwnd, SB_VERT, 8192, TRUE) ;
          
               // Get number of MIDI output devices and set up menu
          
          if (0 == (iNumDevs = midiOutGetNumDevs ()))
          {
               MessageBeep (MB_ICONSTOP) ;
               MessageBox (hwnd, TEXT ("No MIDI output devices!"),
                                 szAppName, MB_OK | MB_ICONSTOP) ;
               return -1 ;
          }
          SetMenu (hwnd, CreateTheMenu (iNumDevs)) ;
          return 0 ;
          
     case WM_SIZE:
          cxClient = LOWORD (lParam) ;
          cyClient = HIWORD (lParam) ;
          
          xOffset = (cxClient - 25 * 3 * cxCaps / 2) / 2 ;
          yOffset = (cyClient - 11 * cyChar) / 2 + 5 * cyChar ;
          return 0 ;
          
     case WM_COMMAND:
          hMenu = GetMenu (hwnd) ;
          
              // "Open" menu command
          
          if (LOWORD (wParam) == IDM_OPEN && !bOpened)
          {
               if (midiOutOpen (&hMidiOut, iDevice, 0, 0, 0))
               {
                    MessageBeep (MB_ICONEXCLAMATION) ;
                    MessageBox (hwnd, TEXT ("Cannot open MIDI device"),
                                szAppName, MB_OK | MB_ICONEXCLAMATION) ;
               }
               else
               {
                    CheckMenuItem (hMenu, IDM_OPEN,  MF_CHECKED) ;
                    CheckMenuItem (hMenu, IDM_CLOSE, MF_UNCHECKED) ;
                    
                    MidiSetPatch (hMidiOut, iChannel, iVoice) ;
                    bOpened = TRUE ;
               }
          }
          
               // "Close" menu command
          
          else if (LOWORD (wParam) == IDM_CLOSE && bOpened)
          {
               CheckMenuItem (hMenu, IDM_OPEN,  MF_UNCHECKED) ;
               CheckMenuItem (hMenu, IDM_CLOSE, MF_CHECKED) ;
               
                    // Turn all keys off and close device
               
               for (i = 0 ; i < 16 ; i++)
                    MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
               
               midiOutClose (hMidiOut) ;
               bOpened = FALSE ;
          }
          
               // Change MIDI "Device" menu command
          
          else if (LOWORD (wParam) >= IDM_DEVICE - 1 && 
                   LOWORD (wParam) <  IDM_CHANNEL)
          {
               CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_UNCHECKED) ;
               iDevice = LOWORD (wParam) - IDM_DEVICE ;
               CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_CHECKED) ;
               
                    // Close and reopen MIDI device
               
               if (bOpened)
               {
                    SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
                    SendMessage (hwnd, WM_COMMAND, IDM_OPEN,  0L) ;
               }
          }
          
               // Change MIDI "Channel" menu command
          
          else if (LOWORD (wParam) >= IDM_CHANNEL && 
                   LOWORD (wParam) <  IDM_VOICE)
          {
               CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_UNCHECKED);
               iChannel = LOWORD (wParam) - IDM_CHANNEL ;
               CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_CHECKED) ;
               
               if (bOpened)
                    MidiSetPatch (hMidiOut, iChannel, iVoice) ;
          }
          
               // Change MIDI "Voice" menu command
          
          else if (LOWORD (wParam) >= IDM_VOICE)
          {
               CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_UNCHECKED) ;
               iVoice = LOWORD (wParam) - IDM_VOICE ;
               CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_CHECKED) ;
               
               if (bOpened)
                    MidiSetPatch (hMidiOut, iChannel, iVoice) ;
          }
          
          InvalidateRect (hwnd, NULL, TRUE) ;
          return 0 ;
          
          // Process a Key Up or Key Down message
          
     case WM_KEYUP:
     case WM_KEYDOWN:
          hdc = GetDC (hwnd) ;
          
          if (bOpened)
               ProcessKey (hdc, message, lParam) ;
          
          ReleaseDC (hwnd, hdc) ;
          return 0 ;
          
          // For Escape, turn off all notes and repaint
          
     case WM_CHAR:
          if (bOpened && wParam == 27)
          {
               for (i = 0 ; i < 16 ; i++)
                    MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
               
               InvalidateRect (hwnd, NULL, TRUE) ;
          }
          return 0 ;
          
          // Horizontal scroll: Velocity
          
     case WM_HSCROLL:
          switch (LOWORD (wParam))
          {
          case SB_LINEUP:         iVelocity -= 1 ;  break ;
          case SB_LINEDOWN:       iVelocity += 1 ;  break ;
          case SB_PAGEUP:         iVelocity -= 8 ;  break ;
          case SB_PAGEDOWN:       iVelocity += 8 ;  break ;
          case SB_THUMBPOSITION:  iVelocity = HIWORD (wParam) ;  break ;
          default:                return 0 ;
          }
          iVelocity = max (1, min (iVelocity, 127)) ;
          SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ;
          return 0 ;
          
          // Vertical scroll:  Pitch Bend
     
     case WM_VSCROLL:
          switch (LOWORD (wParam))
          {
          case SB_THUMBTRACK:    iPitchBend = 16383 - HIWORD (wParam) ;  break ;
          case SB_THUMBPOSITION: iPitchBend = 8191 ;                     break ;
          default:               return 0 ;
          }
          iPitchBend = max (0, min (iPitchBend, 16383)) ;
          SetScrollPos (hwnd, SB_VERT, 16383 - iPitchBend, TRUE) ;
          
          if (bOpened)
               MidiPitchBend (hMidiOut, iChannel, iPitchBend) ;
          return 0 ;
     
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          for (i = 0 ; i < NUMSCANS ; i++)
               if (key[i].xPos != -1)
                    DrawKey (hdc, i, FALSE) ;
               
          midiOutGetDevCaps (iDevice, &moc, sizeof (MIDIOUTCAPS)) ;
          wsprintf (szBuffer, TEXT ("Channel %i"), iChannel + 1) ;
     
          TextOut (hdc, cxCaps, 1 * cyChar, 
                        bOpened ? TEXT ("Open") : TEXT ("Closed"),
                        bOpened ? 4 : 6) ;
          TextOut (hdc, cxCaps, 2 * cyChar, moc.szPname,
                        lstrlen (moc.szPname)) ;
          TextOut (hdc, cxCaps, 3 * cyChar, szBuffer, lstrlen (szBuffer)) ;
          TextOut (hdc, cxCaps, 4 * cyChar,
                        fam[iVoice / 8].inst[iVoice % 8].szInst,
               lstrlen (fam[iVoice / 8].inst[iVoice % 8].szInst)) ;
     
          EndPaint (hwnd, &ps) ;
          return 0 ;
               
     case WM_DESTROY :
          SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
 

效果展示:

c语言高级主题 作者Charles Petzold, 1998年编写的各种关于乐器,通过程序合成实现同样声音效果 我只更改文字显示部分,对于初学者以及部分计算机学生有很好的参考价值。_第1张图片

你可能感兴趣的:(c语言,开发语言)