home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / audio / reverse / reverse.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  19KB  |  653 lines

  1. /**************************************************************************
  2.  *
  3.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6.  *  PURPOSE.
  7.  *
  8.  *  Copyright (C) 1993 - 1997  Microsoft Corporation.  All Rights Reserved.
  9.  *
  10.  **************************************************************************/
  11. /* reverse.c - WinMain() and WndProc() for REVERSE, along with
  12.  * initialization and support code.
  13.  *
  14.  * REVERSE is a Windows sample application that illustrates how to use
  15.  * the low-level waveform playback services. It also shows how to use
  16.  * the multimedia file I/O services to read data from a WAVE file.
  17.  *
  18.  * REVERSE plays a WAVE waveform audio file backwards.
  19.  */
  20.  
  21. #include <windows.h>
  22. #include <windowsx.h>
  23. #include <mmsystem.h>
  24. #include "reverse.h"
  25. #include "strings.h"
  26.  
  27. #define MAX_FILENAME_SIZE 128
  28.  
  29. /* Global variables.
  30.  */
  31. char     szAppName[]    = "Reverse";   // application name
  32. HANDLE    hInstApp = NULL;     // instance handle
  33. HWND     hwndApp     = NULL;     // main window handle
  34. HWND     hwndName = NULL;     // filename window handle
  35. HWND     hwndPlay = NULL;     // "Play" button window handle
  36. HWND     hwndQuit = NULL;     // "Exit" button window handle
  37. HWAVEOUT hWaveOut = NULL;
  38. LPWAVEHDR   lpWaveHdr   = NULL;
  39. HPSTR    lpData      = NULL;     // waveform data block
  40. LPSTR    lpstrLoadStrBuf   = NULL;
  41.  
  42. /* WinMain - Entry point for Reverse.
  43.  */
  44. int PASCAL WinMain(
  45. HINSTANCE hInst,
  46. HINSTANCE hPrev,
  47. LPSTR szCmdLine,
  48. int cmdShow)
  49. {
  50.    MSG      msg;
  51.    WNDCLASS wc;
  52.  
  53.    hInstApp =  hInst;
  54.  
  55.    /* Define and register a window class for the main window.
  56.     */
  57.    if (!hPrev)
  58.    {
  59.       wc.hCursor     = LoadCursor(NULL, IDC_ARROW);
  60.       wc.hIcon    = LoadIcon(hInst, szAppName);
  61.       wc.lpszMenuName      = szAppName;
  62.       wc.lpszClassName  = szAppName;
  63.       wc.hbrBackground  = GetStockBrush(LTGRAY_BRUSH);
  64.       wc.hInstance      = hInst;
  65.       wc.style    = 0;
  66.       wc.lpfnWndProc    = WndProc;
  67.       wc.cbWndExtra     = 0;
  68.       wc.cbClsExtra     = 0;
  69.  
  70.       if (!RegisterClass(&wc))
  71.          return FALSE;
  72.    }
  73.    // Keep string memory out of WIN16 DS.
  74.    lpstrLoadStrBuf = GlobalAllocPtr(GMEM_MOVEABLE, LOADSTRBUFSIZE);
  75.    if(lpstrLoadStrBuf == NULL)
  76.    {
  77.       // don't use LoadString here; it fails when memory is low
  78.       MessageBox(NULL, GetStringRes(IDS_NOMEM),
  79.             NULL, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  80.       return FALSE;
  81.    }
  82.    /* Create and show the main window.
  83.     */
  84.    LoadString(hInstApp, IDS_REVERSEWNDTITLE, lpstrLoadStrBuf,
  85.       LOADSTRBUFSIZE);
  86.    hwndApp = CreateWindow (szAppName,  // class name
  87.          lpstrLoadStrBuf,  // title from string resource
  88.          WS_OVERLAPPEDWINDOW, // style bits
  89.          CW_USEDEFAULT,    // x position
  90.          CW_USEDEFAULT,    // y position
  91.          WMAIN_DX,      // x size
  92.          WMAIN_DY,      // y size
  93.          (HWND)NULL,    // parent window
  94.          (HMENU)NULL,      // use class menu
  95.          (HANDLE)hInst,    // instance handle
  96.          (LPSTR)NULL    // no params to pass on
  97.          );
  98.    /* Create child windows for the "Play" and "Exit" buttons
  99.     * and for an edit field to enter filenames.
  100.     */
  101.    LoadString(hInstApp, IDS_PLAYBUTTONTEXT, lpstrLoadStrBuf,
  102.       LOADSTRBUFSIZE);
  103.    hwndPlay = CreateWindow( "BUTTON", lpstrLoadStrBuf,
  104.          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
  105.          PLAY_X, PLAY_Y,
  106.          PLAY_DX, PLAY_DY,
  107.          hwndApp, (HMENU)IDB_PLAY, hInstApp, NULL );
  108.    if( !hwndPlay )
  109.       return( FALSE );
  110.  
  111.    LoadString(hInstApp, IDS_EXITBUTTONTEXT, lpstrLoadStrBuf,
  112.       LOADSTRBUFSIZE);
  113.    hwndQuit = CreateWindow( "BUTTON", lpstrLoadStrBuf,
  114.          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
  115.          QUIT_X, QUIT_Y,
  116.          QUIT_DX, QUIT_DY,
  117.          hwndApp, (HMENU)IDB_QUIT, hInstApp, NULL );
  118.    if( !hwndQuit )
  119.       return( FALSE );
  120.  
  121.    hwndName = CreateWindow("EDIT","",
  122.          WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOHSCROLL,
  123.          NAME_X, NAME_Y,
  124.          NAME_DX, NAME_DY,
  125.          hwndApp, (HMENU)IDE_NAME, hInstApp, NULL);
  126.    if( !hwndName )
  127.       return( FALSE );
  128.    Edit_LimitText(hwndName, MAX_FILENAME_SIZE - 1);
  129.  
  130.    ShowWindow(hwndApp,cmdShow);
  131.  
  132.    /* Add about dialog to system menu.
  133.     */
  134.    LoadString(hInstApp, IDS_ABOUTMENUTEXT, lpstrLoadStrBuf,
  135.       LOADSTRBUFSIZE);
  136.    AppendMenu(GetSystemMenu(hwndApp, FALSE),
  137.       MF_STRING | MF_ENABLED, IDM_ABOUT, lpstrLoadStrBuf);
  138.  
  139.  
  140.    /* The main message processing loop. Nothing special here.
  141.    */
  142.    while (GetMessage(&msg,NULL,0,0))
  143.    {
  144.       TranslateMessage(&msg);
  145.       DispatchMessage(&msg);
  146.    }
  147.  
  148.    return msg.wParam;
  149. }
  150.  
  151.  
  152. /* WndProc - Main window procedure function.
  153.  */
  154. LONG FAR PASCAL WndProc(
  155. HWND hWnd,
  156. UINT msg,
  157. WPARAM wParam,
  158. LPARAM lParam)
  159. {
  160.    switch (msg)
  161.    {
  162.       HANDLE_MSG(hWnd, WM_DESTROY, ReverseOnDestroy);
  163.  
  164.       HANDLE_MSG(hWnd, WM_SYSCOMMAND, ReverseOnSysCommand);
  165.  
  166.       case WM_SETFOCUS:
  167.          SetFocus(hwndName);
  168.          return 0;
  169.  
  170.       HANDLE_MSG(hWnd, WM_COMMAND, ReverseOnCommand);
  171.       case MM_WOM_DONE:
  172.          /* This message indicates a waveform data block has
  173.           * been played and can be freed. Clean up the
  174.           * preparation done previously on the header.
  175.           */
  176.          waveOutUnprepareHeader( (HWAVEOUT) wParam,
  177.             (LPWAVEHDR) lParam, sizeof(WAVEHDR) );
  178.  
  179.          /* free all memory associated with the data block
  180.           */
  181.          cleanup();
  182.          /* Close the waveform output device.
  183.           */
  184.          waveOutClose( (HWAVEOUT) wParam );
  185.          hWaveOut = NULL;
  186.  
  187.          /* Reenable both button controls.
  188.           */
  189.          EnableWindow( hwndPlay, TRUE );
  190.          EnableWindow( hwndQuit, TRUE );
  191.          SetFocus(hwndName);
  192.  
  193.          break;
  194.    }
  195.    return MyDefProc(hWnd,msg,wParam,lParam);
  196. }
  197.  
  198. void ReverseOnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  199. {
  200.    /* Process messages sent by the child window controls.
  201.     */
  202.    switch (id)
  203.    {
  204.       case IDE_NAME:    // filename edit control
  205.          return;
  206.  
  207.       case IDB_PLAY: // "Play" button
  208.          if (codeNotify == BN_CLICKED)
  209.             ReversePlay();
  210.          break;
  211.  
  212.       case IDB_QUIT: // "Exit" button
  213.          if (codeNotify == BN_CLICKED)
  214.             PostQuitMessage(0);
  215.          break;
  216.    }
  217.    FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, MyDefProc);
  218. }
  219.  
  220. void ReverseOnDestroy(
  221. HWND hwnd)
  222. {
  223.    if(lpstrLoadStrBuf)
  224.       GlobalFreePtr(lpstrLoadStrBuf);
  225.    if (hWaveOut)
  226.    {
  227.       waveOutReset(hWaveOut);
  228.       waveOutUnprepareHeader(hWaveOut, lpWaveHdr,
  229.          sizeof(WAVEHDR) );
  230.       cleanup();
  231.       waveOutClose(hWaveOut);
  232.    }
  233.    PostQuitMessage(0);
  234.    FORWARD_WM_DESTROY(hwnd, MyDefProc);
  235. }
  236.  
  237. void ReverseOnSysCommand(
  238. HWND hwnd,
  239. UINT cmd,
  240. int x,
  241. int y)
  242. {
  243.    switch (cmd)
  244.    {
  245.       case IDM_ABOUT:
  246.          /* Show ABOUTBOX dialog box.
  247.           */
  248.          DialogBox(hInstApp, "ABOUTBOX", hwnd, AppAbout);
  249.          break;
  250.    }
  251.    FORWARD_WM_SYSCOMMAND(hwnd, cmd, x, y, MyDefProc);
  252. }
  253.  
  254.  
  255.  
  256. /* AppAbout -- Dialog procedure for ABOUTBOX dialog box.
  257.  */
  258. BOOL FAR PASCAL AppAbout(
  259. HWND hDlg,
  260. UINT msg,
  261. WPARAM wParam,
  262. LPARAM lParam)
  263. {
  264.    switch (msg)
  265.    {
  266.       case WM_COMMAND:
  267.          if (GET_WM_COMMAND_ID(wParam, lParam) == IDOK
  268.             || GET_WM_COMMAND_ID(wParam, lParam)
  269.             == IDCANCEL)
  270.             EndDialog(hDlg,TRUE);
  271.          break;
  272.  
  273.       case WM_INITDIALOG:
  274.          return TRUE;
  275.    }
  276.    return FALSE;
  277. }
  278.  
  279.  
  280.  
  281. /* ReversePlay - Gets a filename from the edit control, then uses
  282.  * the multimedia file I/O services to read data from the requested
  283.  * WAVE file. If the file is a proper WAVE file, ReversePlay() calls
  284.  * the Interchange() function to reverse the order of the waveform
  285.  * samples in the file. It then plays the reversed waveform data.
  286.  *
  287.  * Note that ReversePlay() only handles a single waveform data block.
  288.  * If the requested WAVE file will not fit in a single data block, it
  289.  * will not be played. The size of a single data block depends on the
  290.  * amount of available system memory.
  291.  *
  292.  * Params: void
  293.  *
  294.  * Return: void
  295.  */
  296. void ReversePlay()
  297. {
  298.    HMMIO          hmmio;
  299.    MMCKINFO       mmckinfoParent;
  300.    MMCKINFO       mmckinfoSubchunk;
  301.    DWORD          dwFmtSize;
  302.    char           szFileName[ MAX_FILENAME_SIZE ];
  303.    DWORD          dwResult;
  304.    HANDLE         hFormat;
  305.    WAVEFORMATEX   *pFormat;
  306.    DWORD          dwDataSize;
  307.    HPSTR          hpch1, hpch2;
  308.    WORD           wBlockSize;
  309.    HANDLE         hData    = NULL;
  310.  
  311.    /* Get the filename from the edit control.
  312.     */
  313.    if (!GetWindowText( hwndName, (LPSTR)szFileName, MAX_FILENAME_SIZE))
  314.    {
  315.       LoadString(hInstApp, IDS_FAILEDTOGETFNAME, lpstrLoadStrBuf,
  316.          LOADSTRBUFSIZE);
  317.       MessageBox(hwndApp, lpstrLoadStrBuf,
  318.             NULL, MB_OK | MB_ICONEXCLAMATION);
  319.       return;
  320.    }
  321.  
  322.    /* Open the given file for reading using buffered I/O.
  323.     */
  324.    if(!(hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF)))
  325.    {
  326.       LoadString(hInstApp, IDS_FAILEDTOOPENFILE, lpstrLoadStrBuf,
  327.          LOADSTRBUFSIZE);
  328.       MessageBox(hwndApp, lpstrLoadStrBuf,
  329.             NULL, MB_OK | MB_ICONEXCLAMATION);
  330.       return;
  331.    }
  332.  
  333.    /* Locate a 'RIFF' chunk with a 'WAVE' form type
  334.     * to make sure it's a WAVE file.
  335.     */
  336.    mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  337.    if (mmioDescend(hmmio, &mmckinfoParent, NULL, MMIO_FINDRIFF))
  338.    {
  339.       LoadString(hInstApp, IDS_NOTAWAVEFILE, lpstrLoadStrBuf,
  340.          LOADSTRBUFSIZE);
  341.       MessageBox(hwndApp, lpstrLoadStrBuf,
  342.             NULL, MB_OK | MB_ICONEXCLAMATION);
  343.       mmioClose(hmmio, 0);
  344.       return;
  345.    }
  346.  
  347.    /* Now, find the format chunk (form type 'fmt '). It should be
  348.     * a subchunk of the 'RIFF' parent chunk.
  349.     */
  350.    mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
  351.    if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
  352.       MMIO_FINDCHUNK))
  353.    {
  354.       LoadString(hInstApp, IDS_WAVEFILECORRUPT, lpstrLoadStrBuf,
  355.          LOADSTRBUFSIZE);
  356.       MessageBox(hwndApp, lpstrLoadStrBuf,
  357.             NULL, MB_OK | MB_ICONEXCLAMATION);
  358.       mmioClose(hmmio, 0);
  359.       return;
  360.    }
  361.  
  362.    /* Get the size of the format chunk, allocate and lock memory for it.
  363.     */
  364.    dwFmtSize = mmckinfoSubchunk.cksize;
  365.    hFormat = LocalAlloc(LMEM_MOVEABLE, LOWORD(dwFmtSize));
  366.    if (!hFormat)
  367.    {
  368.       MessageBox(hwndApp, GetStringRes(IDS_NOMEM),
  369.             NULL, MB_OK | MB_ICONEXCLAMATION);
  370.       mmioClose(hmmio, 0);
  371.       return;
  372.    }
  373.    pFormat = (WAVEFORMATEX *) LocalLock(hFormat);
  374.    if (!pFormat)
  375.    {
  376.       MessageBox(hwndApp, GetStringRes(IDS_NOMEM_LK),
  377.             NULL, MB_OK | MB_ICONEXCLAMATION);
  378.       LocalFree( hFormat );
  379.       mmioClose(hmmio, 0);
  380.       return;
  381.    }
  382.  
  383.    /* Read the format chunk.
  384.     */
  385.    if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LONG) dwFmtSize)
  386.    {
  387.       LoadString(hInstApp, IDS_FAILEDREADFMTCHNK, lpstrLoadStrBuf,
  388.          LOADSTRBUFSIZE);
  389.       MessageBox(hwndApp, lpstrLoadStrBuf,
  390.             NULL, MB_OK | MB_ICONEXCLAMATION);
  391.       LocalUnlock( hFormat );
  392.       LocalFree( hFormat );
  393.       mmioClose(hmmio, 0);
  394.       return;
  395.    }
  396.  
  397.    /* Make sure it's a PCM file.
  398.     */
  399.    if (pFormat->wFormatTag != WAVE_FORMAT_PCM)
  400.    {
  401.       LocalUnlock( hFormat );
  402.       LocalFree( hFormat );
  403.       mmioClose(hmmio, 0);
  404.       LoadString(hInstApp, IDS_NOTAPCMFILE, lpstrLoadStrBuf,
  405.          LOADSTRBUFSIZE);
  406.       MessageBox(hwndApp, lpstrLoadStrBuf,
  407.             NULL, MB_OK | MB_ICONEXCLAMATION);
  408.       return;
  409.    }
  410.  
  411.    /* Make sure a waveform output device supports this format.
  412.     */
  413.    #if (WINVER >= 0x0400)
  414.    if (waveOutOpen(&hWaveOut, WAVE_MAPPER, pFormat, 0, 0L,
  415.                WAVE_FORMAT_QUERY))
  416.         #else
  417.    if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat, 0, 0L,
  418.                WAVE_FORMAT_QUERY))
  419.         #endif
  420.    {
  421.       LocalUnlock( hFormat );
  422.       LocalFree( hFormat );
  423.       mmioClose(hmmio, 0);
  424.       LoadString(hInstApp, IDS_CANTPLAYFORMAT, lpstrLoadStrBuf,
  425.          LOADSTRBUFSIZE);
  426.       MessageBox(hwndApp, lpstrLoadStrBuf,
  427.             NULL, MB_OK | MB_ICONEXCLAMATION);
  428.       return;
  429.    }
  430.  
  431.    /* Ascend out of the format subchunk.
  432.     */
  433.    mmioAscend(hmmio, &mmckinfoSubchunk, 0);
  434.  
  435.    /* Find the data subchunk.
  436.     */
  437.    mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
  438.    if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
  439.       MMIO_FINDCHUNK))
  440.    {
  441.       LoadString(hInstApp, IDS_NODATACHUNK, lpstrLoadStrBuf,
  442.          LOADSTRBUFSIZE);
  443.       MessageBox(hwndApp, lpstrLoadStrBuf,
  444.             NULL, MB_OK | MB_ICONEXCLAMATION);
  445.       LocalUnlock( hFormat );
  446.       LocalFree( hFormat );
  447.       mmioClose(hmmio, 0);
  448.       return;
  449.    }
  450.  
  451.    /* Get the size of the data subchunk.
  452.     */
  453.    dwDataSize = mmckinfoSubchunk.cksize;
  454.    if (dwDataSize == 0L)
  455.    {
  456.       LoadString(hInstApp, IDS_CHUNKHASNODATA, lpstrLoadStrBuf,
  457.          LOADSTRBUFSIZE);
  458.       MessageBox(hwndApp, lpstrLoadStrBuf,
  459.             NULL, MB_OK | MB_ICONEXCLAMATION);
  460.       LocalUnlock( hFormat );
  461.       LocalFree( hFormat );
  462.       mmioClose(hmmio, 0);
  463.       return;
  464.    }
  465.  
  466.    /* Open a waveform output device.
  467.     */
  468.    #if (WINVER >= 0x0400)
  469.    if (waveOutOpen(&hWaveOut, WAVE_MAPPER,
  470.       pFormat, (UINT)hwndApp, 0L, CALLBACK_WINDOW))
  471.         #else
  472.    if (waveOutOpen(&hWaveOut, WAVE_MAPPER,
  473.       (LPWAVEFORMAT)pFormat, (UINT)hwndApp, 0L, CALLBACK_WINDOW))
  474.         #endif
  475.    {
  476.       LoadString(hInstApp, IDS_FAILEDOPENDEVICE, lpstrLoadStrBuf,
  477.          LOADSTRBUFSIZE);
  478.       MessageBox(hwndApp, lpstrLoadStrBuf,
  479.             NULL, MB_OK | MB_ICONEXCLAMATION);
  480.       LocalUnlock( hFormat );
  481.       LocalFree( hFormat );
  482.       mmioClose(hmmio, 0);
  483.       return;
  484.    }
  485.  
  486.    /* Save block alignment info for later use.
  487.     */
  488.    wBlockSize = pFormat->nBlockAlign;
  489.  
  490.    /* We're done with the format header, free it.
  491.     */
  492.    LocalUnlock( hFormat );
  493.    LocalFree( hFormat );
  494.  
  495.    /* Allocate and lock memory for the waveform data.
  496.     */
  497.    lpData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
  498.    if (!lpData)
  499.    {
  500.       MessageBox(hwndApp, GetStringRes(IDS_NOMEM_DT),
  501.             NULL, MB_OK | MB_ICONEXCLAMATION);
  502.       mmioClose(hmmio, 0);
  503.       return;
  504.    }
  505.  
  506.    /* Read the waveform data subchunk.
  507.     */
  508.    if(mmioRead(hmmio, lpData, dwDataSize) != (LONG) dwDataSize)
  509.    {
  510.       LoadString(hInstApp, IDS_FAILEDREADCHUNK, lpstrLoadStrBuf,
  511.          LOADSTRBUFSIZE);
  512.       MessageBox(hwndApp, lpstrLoadStrBuf,
  513.             NULL, MB_OK | MB_ICONEXCLAMATION);
  514.       GlobalFreePtr( lpData );
  515.       mmioClose(hmmio, 0);
  516.       return;
  517.    }
  518.  
  519.    /* We're done with the file, close it.
  520.     */
  521.    mmioClose(hmmio, 0);
  522.  
  523.    /* Reverse the sound for playing.
  524.     */
  525.    hpch1 = lpData;
  526.    hpch2 = lpData + dwDataSize - 1;
  527.    while (hpch1 < hpch2)
  528.    {
  529.       Interchange( hpch1, hpch2, wBlockSize );
  530.       hpch1 += wBlockSize;
  531.       hpch2 -= wBlockSize;
  532.    }
  533.  
  534.    /* Allocate a waveform data header. The WAVEHDR must be
  535.     * globally allocated and locked.
  536.     */
  537.    lpWaveHdr = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  538.                   (DWORD) sizeof(WAVEHDR));
  539.    if (!lpWaveHdr)
  540.    {
  541.       GlobalFreePtr( lpData );
  542.       MessageBox(hwndApp, GetStringRes(IDS_NOMEM_HR),
  543.             NULL, MB_OK | MB_ICONEXCLAMATION);
  544.       return;
  545.    }
  546.  
  547.    /* If you need instance data for a waveform data block, allocate some
  548.     * memory and store the pointer in lpWaveHdr->dwUser, before the call
  549.     * to waveOutPrepareHeader(). The code inside the #if 0 / #endif, and
  550.     * the commented-out lpWaveHdr->dwUser = ... illustrate this.
  551.     * Don't forget to free the instance memory when you're done with it,
  552.     * or on error bailout.
  553.     */
  554.    #if 0
  555.    lpYourData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, sizeof(YOURDATA));
  556.    if (!lpYourData)
  557.    {
  558.       GlobalFreePtr( lpData );
  559.       GlobalFreePtr( lpWaveHdr );
  560.       MessageBox(hwndApp, GetStringRes(IDS_NOMEM_IS),
  561.             NULL, MB_OK | MB_ICONEXCLAMATION);
  562.       return;
  563.    }
  564.    #endif
  565.    /* Set up WAVEHDR structure and prepare it to be written to wave device.
  566.     */
  567.    lpWaveHdr->lpData = lpData;
  568.    lpWaveHdr->dwBufferLength = dwDataSize;
  569.    lpWaveHdr->dwFlags = 0L;
  570.    lpWaveHdr->dwLoops = 0L;
  571.    // lpWaveHdr->dwUser = (DWORD) lpYourData; // save instance data ptr
  572.    if(waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR)))
  573.    {
  574.       cleanup();
  575.       LoadString(hInstApp, IDS_UNABLEPREPAREHDR, lpstrLoadStrBuf,
  576.          LOADSTRBUFSIZE);
  577.       MessageBox(hwndApp, lpstrLoadStrBuf,
  578.             NULL, MB_OK | MB_ICONEXCLAMATION);
  579.  
  580.       return;
  581.    }
  582.  
  583.    /* Then the data block can be sent to the output device.
  584.     */
  585.    dwResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
  586.    if (dwResult != 0)
  587.    {
  588.       waveOutUnprepareHeader( hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
  589.       cleanup();
  590.       LoadString(hInstApp, IDS_FAILEDWRITEDEVICE, lpstrLoadStrBuf,
  591.          LOADSTRBUFSIZE);
  592.       MessageBox(hwndApp, lpstrLoadStrBuf,
  593.             NULL, MB_OK | MB_ICONEXCLAMATION);
  594.       return;
  595.    }
  596.  
  597.    /* Disable input to the button controls.
  598.     */
  599.    EnableWindow(hwndPlay, FALSE);
  600.    EnableWindow(hwndQuit, FALSE);
  601. }
  602.  
  603. /* Interchange - Interchanges two samples at the given positions.
  604.  *
  605.  * Params:  hpchPos1 - Points to one sample.
  606.  *       hpchPos2 - Points to the other sample.
  607.  *       wLength  - The length of a sample in bytes.
  608.  *
  609.  * Return: void
  610.  */
  611. void Interchange(
  612. HPSTR hpchPos1,
  613. HPSTR hpchPos2,
  614. WORD wLength)
  615. {
  616.    WORD  wPlace;
  617.    BYTE  bTemp;
  618.  
  619.    for (wPlace = 0; wPlace < wLength; wPlace++)
  620.    {
  621.       bTemp = hpchPos1[wPlace];
  622.       hpchPos1[wPlace] = hpchPos2[wPlace];
  623.       hpchPos2[wPlace] = bTemp;
  624.    }
  625. }
  626.  
  627. VOID cleanup(void)
  628. {
  629.    // if you add wave instance data, this is a good place to free it.
  630.    GlobalFreePtr( lpWaveHdr );
  631.    GlobalFreePtr( lpData );
  632. }
  633.  
  634.  
  635. /******************************************************************************\
  636. *
  637. *  FUNCTION:    GetStringRes (int id INPUT ONLY)
  638. *
  639. *  COMMENTS:    Load the resource string with the ID given, and return a
  640. *               pointer to it.  Notice that the buffer is common memory so
  641. *               the string must be used before this call is made a second time.
  642. *
  643. \******************************************************************************/
  644.  
  645. LPTSTR   GetStringRes (int id)
  646. {
  647.   static TCHAR buffer[MAX_PATH];
  648.  
  649.   buffer[0]=0;
  650.   LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  651.   return buffer;
  652. }
  653.