home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / directx2 / sdk / samples / dsstream / dsstream.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-28  |  56.8 KB  |  1,407 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:   dsstream.c
  6.  *  Content:   Illustrates streaming data from a disk WAVE file to a
  7.  *             DirectSound secondary buffer for playback.
  8.  *
  9.  ***************************************************************************/
  10. #define WIN32_LEAN_AND_MEAN
  11. #include <windows.h>
  12. #include <windowsx.h>
  13. #include <mmsystem.h>
  14. #include <dsound.h>
  15. #include <commctrl.h>
  16. #include <commdlg.h>
  17. #include <memory.h>
  18. #include <cderr.h>
  19.  
  20. #include "dsstream.h"
  21.  
  22. char szAppClass[] = "DSStreamWndClass";
  23. char szAppName[]  = "DSStream";
  24.  
  25. char szAppTitle[64];
  26. char szAppCaption[64];
  27. char szPan[32];
  28. char szVolume[32];
  29. char szFrequency[32];
  30. char szProgress[32];
  31. char szOpenFilter[128];
  32. char szOpenDLGTitle[64];
  33.  
  34.  
  35. char szTemp[256];
  36. char szDebug[128];
  37. char szFileBuffer[MAX_PATH];
  38. char szFileTitle[MAX_PATH];
  39.  
  40. LPDIRECTSOUND           lpDS = NULL;
  41. LPDIRECTSOUNDBUFFER     lpDSBStreamBuffer = NULL;
  42.  
  43. WAVEINFOCA              wiWave;
  44.  
  45. HWND    hWndMain, hWndPan, hWndPanText, hWndVol, hWndVolText, hWndFreqText;
  46. HWND    hWndBar, hWndPlay, hWndStop, hWndLoopCheck, hWndFreq, hWndProg;
  47. HWND    hWndProgText;
  48.  
  49. #ifdef DEBUG
  50. HWND                    hWndList;
  51. #endif
  52.  
  53. HINSTANCE       hInst;
  54.  
  55. static BOOL     bFileOpen = FALSE, bPlaying = FALSE, bTimerInstalled = FALSE;
  56. static BOOL     bEnumDrivers = FALSE;
  57. static UINT     uTimerID = 0, uLastPercent = 100;
  58. static GUID     guID;
  59.  
  60. static BOOL InitApp( HINSTANCE );
  61. static BOOL InitInstance( HINSTANCE, int );
  62. static void BuildTitleBarText( void );
  63.  
  64. /******************************************************************************
  65.  * WinMain()
  66.  *
  67.  * Entry point for all Windows programs - performs initialization, message loop
  68.  */
  69. int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  70.                                         LPSTR lpCmdLine, int nCmdShow )
  71.     {
  72.     MSG     msg;
  73.  
  74.     hInst = hInstance;
  75.  
  76.     /* Make sure the common controls are loaded for our use */
  77.     InitCommonControls();
  78.  
  79.     if( !hPrevInstance )
  80.         if( !InitApp( hInstance ))
  81.             {
  82.             ErrorMessageBox( IDS_ERROR_APPINIT, MB_ICONSTOP );
  83.             return( FALSE );
  84.             }
  85.  
  86.     if( !InitInstance( hInstance, nCmdShow ))
  87.         {
  88.         ErrorMessageBox( IDS_ERROR_INSTANCEINIT, MB_ICONSTOP );
  89.         return( FALSE );
  90.         }
  91.  
  92.     while( GetMessage((LPMSG)&msg, NULL, 0, 0 ))
  93.         {
  94.         TranslateMessage( &msg );
  95.         DispatchMessage( &msg );
  96.         }
  97.  
  98.     UnregisterClass( szAppClass, hInstance );
  99.     return( msg.wParam );
  100.     } /* End of WinMain() */
  101.  
  102.  
  103. /*****************************************************************************/
  104. /* InitApp()                                                                 */
  105. /*                                                                           */
  106. /*   Inits things that only need to be created once for the this application */
  107. /* (like creating the window class).                                         */
  108. /*****************************************************************************/
  109. static BOOL InitApp( HINSTANCE hInstance )
  110.     {
  111.     WNDCLASS    wc;
  112.  
  113.     /* Set up and register a window class */
  114.     wc.style            = CS_HREDRAW | CS_VREDRAW;
  115.     wc.lpszClassName    = szAppClass;
  116.     wc.lpfnWndProc      = (WNDPROC)MainWindowProc;
  117.     wc.cbClsExtra       = 0;
  118.     wc.cbWndExtra       = sizeof( DWORD );
  119.     wc.hInstance        = hInstance;
  120.     wc.hIcon            = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON1 ));
  121.     wc.hCursor          = LoadCursor( NULL, IDC_ARROW );
  122.     wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW);
  123.     wc.lpszMenuName     = MAKEINTRESOURCE( IDR_MAINMENU );
  124.  
  125.     if( !RegisterClass( &wc ))
  126.         {
  127.         ErrorMessageBox( IDS_ERROR_REGISTERCLASS, MB_ICONSTOP );
  128.         return( FALSE );
  129.         }
  130.     return( TRUE );
  131.     } /* End of InitApp() */
  132.  
  133.  
  134. /*****************************************************************************/
  135. /* InitInstance()                                                            */
  136. /*                                                                           */
  137. /* Performs initialization that must be done for each application instance.  */
  138. /*                                                                           */
  139. /*****************************************************************************/
  140. static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
  141.     {
  142.     HWND        hWnd;
  143.     HRESULT     dsRetVal;
  144.     RECT        crect;
  145.     int         cx, cy;
  146.     UINT        uCharsRead;
  147.  
  148.     DbgInitialize( TRUE );
  149.  
  150.     LoadString( hInstance, IDS_APP_TITLE, szAppTitle, sizeof(szAppTitle));
  151.     LoadString( hInstance, IDS_APP_CAPTION, szAppCaption, sizeof(szAppCaption));
  152.     LoadString( hInstance, IDS_TBTITLE_PAN, szPan, sizeof(szPan));
  153.     LoadString( hInstance, IDS_TBTITLE_VOLUME, szVolume, sizeof(szVolume));
  154.     LoadString( hInstance, IDS_TBTITLE_FREQUENCY,
  155.                                             szFrequency, sizeof(szFrequency));
  156.     LoadString( hInstance, IDS_TBTITLE_PROGRESS,
  157.                                             szProgress, sizeof(szProgress));
  158.     LoadString( hInstance, IDS_OPEN_DLGTITLE,
  159.                                         szOpenDLGTitle, sizeof(szOpenDLGTitle));
  160. /* This is a little trick designed to allow us to load a common dialog box
  161.  * filter string, which is really a concatentation of several NULL-terminated
  162.  * strings. Note that while is is possible to enter something else into the
  163.  * resource as placeholders for the NULL characters, this has the undesireable
  164.  * effect of forcing us to search-and-replace byte-by-byte and doesn't make it
  165.  * as easy to internationalize our strings...
  166.  */
  167.     memset( szOpenFilter, 0, sizeof(szOpenFilter));
  168.     uCharsRead = LoadString( hInstance, IDS_OPEN_FILTER1,
  169.                                 szOpenFilter, sizeof(szOpenFilter)) + 1;
  170.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER2,
  171.                                 &szOpenFilter[uCharsRead],
  172.                                 sizeof(szOpenFilter) - uCharsRead ) + 1;
  173.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER3,
  174.                                 &szOpenFilter[uCharsRead],
  175.                                 sizeof(szOpenFilter) - uCharsRead ) + 1;
  176.     LoadString( hInstance, IDS_OPEN_FILTER4,
  177.                                 &szOpenFilter[uCharsRead],
  178.                                 sizeof(szOpenFilter) - uCharsRead );
  179.  
  180.     /* Calculate the size of the client window */
  181.     cx = CONTROL_SPACE_CX + 2*BORDER_SPACE_CX + BUTTON_CX + PAN_TB_CX
  182.         + 2*GetSystemMetrics( SM_CXBORDER ) + PAN_TEXT_CX + TEXT_SPACE_CX;
  183.  
  184.     cy = 2*(BORDER_SPACE_CY + GetSystemMetrics( SM_CYBORDER ))
  185.         + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY + PROG_TB_CY
  186.         + GetSystemMetrics( SM_CYMENU ) + 3*CONTROL_SPACE_CY
  187.         + GetSystemMetrics( SM_CYCAPTION );
  188.  
  189.     /* Create an application window */
  190. #ifdef DEBUG
  191.     hWnd = CreateWindow( szAppClass,            /* class name */
  192.                         szAppCaption,           /* caption for window */
  193.                         WS_OVERLAPPEDWINDOW,    /* style */
  194.                         CW_USEDEFAULT,          /* x position */
  195.                         CW_USEDEFAULT,          /* y position */
  196.                         cx,                     /* width */
  197.                         cy + 200,               /* height */
  198.                         NULL,                   /* parent window */
  199.                         NULL,                   /* menu */
  200.                         hInstance,              /* instance */
  201.                         NULL );                 /* parms */
  202. #else
  203.     hWnd = CreateWindow( szAppClass,            /* class name */
  204.                         szAppCaption,           /* caption for window */
  205.                         WS_OVERLAPPEDWINDOW,    /* style */
  206.                         CW_USEDEFAULT,          /* x position */
  207.                         CW_USEDEFAULT,          /* y position */
  208.                         cx,                     /* width */
  209.                         cy,                     /* height */
  210.                         NULL,                   /* parent window */
  211.                         NULL,                   /* menu */
  212.                         hInstance,              /* instance */
  213.                         NULL );                 /* parms */
  214. #endif
  215.  
  216.     if( !hWnd )
  217.         {
  218.         ErrorMessageBox( IDS_ERROR_MAINWNDCREATE, MB_ICONSTOP );
  219.         return( FALSE );
  220.         }
  221.  
  222.     hWndMain = hWnd;
  223.     GetClientRect( hWndMain, &crect );
  224.  
  225. #ifdef DEBUG
  226.     cy = 2*BORDER_SPACE_CY + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY + PROG_TB_CY
  227.                 + 3*CONTROL_SPACE_CY;
  228.  
  229.     hWndList = CreateWindow( "listbox", NULL, WS_CHILD | WS_VISIBLE
  230.                                 | LBS_NOINTEGRALHEIGHT | WS_VSCROLL,
  231.                                 0, cy, crect.right-crect.left,
  232.                                 crect.bottom - crect.top - cy,
  233.                                 hWnd, NULL, hInstance, NULL );
  234. #endif
  235.  
  236.     /* Create some controls for things like volume, panning, etc. */
  237.     if( CreateChildren( crect ))
  238.         return( FALSE );
  239.  
  240.     ShowWindow( hWnd, nCmdShow );
  241.     UpdateWindow( hWnd );
  242.  
  243.     /* Create the main DirectSound object */
  244.  
  245.     if(( bEnumDrivers = GetProfileInt( "DSSTREAM", "EnumDrivers", FALSE )) != FALSE ) {
  246.         if( !DoDSoundEnumerate( &guID )) {
  247.             dsRetVal = DirectSoundCreate( &guID, &lpDS, NULL );
  248.         } else {
  249.             dsRetVal = DirectSoundCreate( NULL, &lpDS, NULL );
  250.         }
  251.     } else {
  252.         dsRetVal = DirectSoundCreate( NULL, &lpDS, NULL );
  253.     }
  254.  
  255.     
  256.     if( dsRetVal != DS_OK )
  257.         {
  258.         ErrorMessageBox( IDS_ERROR_DSCREATE, MB_ICONSTOP );
  259.         return( FALSE );
  260.         }
  261.  
  262.     dsRetVal = lpDS->lpVtbl->SetCooperativeLevel( lpDS,
  263.                                                 hWndMain,
  264.                                                 DSSCL_NORMAL );
  265.     if( dsRetVal != DS_OK )
  266.         {
  267.         ErrorMessageBox( IDS_ERROR_DSCOOPERATIVE, MB_ICONSTOP );
  268.         return( FALSE );
  269.         }
  270.  
  271.  
  272.     return( TRUE );
  273.     } /* End of InitInstance() */
  274.  
  275.  
  276. /****************************************************************************/
  277. /* MainWindowProc()                                                         */
  278. /*                                                                          */
  279. /*    Messages for our main window are handled here                         */
  280. /*                                                                          */
  281. /****************************************************************************/
  282. extern LONG lInTimer;    
  283. LRESULT CALLBACK MainWindowProc( HWND hWnd, unsigned uMsg,
  284.                                                 WPARAM wParam, LPARAM lParam )
  285.     {
  286. #ifndef DEBUG
  287.     LPMINMAXINFO lpMinMax;
  288. #endif
  289.     DWORD   dwCDErr = 0, dwProg;
  290.     float   fPercent;
  291.     UINT    uPercent;
  292.     BOOL    bResult = FALSE;
  293.     int     nChkErr;
  294.     HRESULT dsrval;
  295.  
  296.     switch( uMsg )
  297.         {
  298.         case WM_DSSTREAM_PROGRESS:
  299.             dwProg = (DWORD)lParam;
  300.             dwProg  = dwProg % wiWave.mmckInRIFF.cksize;
  301.             fPercent = (float)((dwProg * 100)
  302.                                         / wiWave.mmckInRIFF.cksize);
  303.             SendMessage( hWndProg, TBM_SETPOS,
  304.                         TRUE, (DWORD)(float)(fPercent*(float)PROG_MULTIPLIER));
  305.             uPercent = (UINT)fPercent;
  306.             if( uPercent != uLastPercent )
  307.                 {
  308.                 uLastPercent = uPercent;
  309.                 wsprintf( szTemp, "%s: %u%%", szProgress, uPercent );
  310.                 Static_SetText( hWndProgText, szTemp );
  311. #ifdef DEBUG
  312.                 ListBox_AddString( hWndList, szTemp );
  313. #endif
  314.                 }
  315.             break;
  316.  
  317.         /*
  318.          *      This message will be posted by the helper DLL when the TimeFunc
  319.          * is done streaming the WAVE file. It serves as notification that the
  320.          * caller should terminate WAVE playback and end the MM timer event.
  321.          */
  322.         case WM_DSSTREAM_DONE:
  323.             /* Emulate a WM_COMMAND to ourselves */
  324.             SendMessage( hWnd, WM_COMMAND, MAKEWPARAM( IDM_STOP, 0 ), 0L );
  325.             break;
  326.  
  327. #ifdef DEBUG
  328.         case WM_DSSTREAM_DEBUG:
  329.             if( LOWORD(wParam) == DEBUGF_PLAYPOSITION )
  330.                 {
  331.                 wsprintf( szDebug, "pp = %li", lParam );
  332.                 ListBox_AddString( hWndList, szDebug );
  333.                 DPF( 4, szDebug );
  334.                 }
  335.             else if( LOWORD(wParam) == DEBUGF_WRITEPOSITION )
  336.                 {
  337.                 wsprintf( szDebug, "wp = %li", lParam );
  338.                 ListBox_AddString( hWndList, szDebug );
  339.                 DPF( 4, szDebug );
  340.                 }
  341.             else if( LOWORD(wParam) == DEBUGF_NEXTWRITE )
  342.                 {
  343.                 wsprintf( szDebug, "nw = %li", lParam );
  344.                 ListBox_AddString( hWndList, szDebug );
  345.                 DPF( 4, szDebug );
  346.                 }
  347.             else if( LOWORD(wParam) == DEBUGF_SKIP )
  348.                 {
  349.                 ListBox_AddString( hWndList, "Skipped segment read" );
  350.                 DPF( 5, szDebug );
  351.                 }
  352.             break;
  353. #endif
  354.  
  355.         case WM_COMMAND:
  356.             switch( LOWORD( wParam ))
  357.                 {
  358.                 case IDM_FILE_OPEN:
  359.                     {
  360.                     OPENFILENAME        ofn;
  361.     /*
  362.      * Clear out and fill in an OPENFILENAME structure in preparation
  363.      * for creating a common dialog box to open a file.
  364.      */
  365.                     memset( &ofn, 0, sizeof(OPENFILENAME));
  366.                     ofn.lStructSize     = sizeof(OPENFILENAME);
  367.                     ofn.hwndOwner       = hWnd;
  368.                     ofn.hInstance       = hInst;
  369.                     ofn.lpstrFilter     = szOpenFilter;
  370.                     ofn.nFilterIndex    = 1;
  371.                     szFileBuffer[0]     = '\0';
  372.                     ofn.lpstrFile       = szFileBuffer;
  373.                     ofn.nMaxFile        = sizeof(szFileBuffer);
  374.                     ofn.lpstrFileTitle  = szFileTitle;
  375.                     ofn.nMaxFileTitle   = sizeof(szFileTitle);
  376.                     ofn.lpstrDefExt     = "WAV";
  377.                     ofn.lpstrTitle      = szOpenDLGTitle;
  378.                     ofn.Flags           = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
  379.  
  380.                     bResult = GetOpenFileName( &ofn ); /* Do the dialog box */
  381.  
  382.     /*
  383.      *  A return of TRUE indicates that the user did not select a filename.
  384.      * The possible reasons are: Cancel was clicked, or an error occured.
  385.      * If Cancel was clicked, the CommDlgExtendedError() function will not
  386.      * return a valid code.  For anything else, an error code will come back.
  387.      */
  388.                     if( bResult == FALSE )
  389.                         {
  390.                         dwCDErr = CommDlgExtendedError();
  391.                         if( dwCDErr )
  392.                             {
  393.                             /* Handle a common dialog box error */
  394.                             HandleCommDlgError( dwCDErr );
  395.                             }
  396.                         else    /* Clicked Cancel, so finish msg processing */
  397.                             return( 0 );
  398.                         }
  399.                     else
  400.                         {
  401.                         if( bFileOpen )
  402.                             {
  403.     /* Need to close the previous file before we open a new one.  The best
  404.      * way to do this is by faking a menu command, so that we only have the
  405.      * actual code in one place and it can easily be changed.
  406.      */
  407.                             SendMessage( hWnd, WM_COMMAND,
  408.                                         MAKEWPARAM( IDM_FILE_CLOSE, 0 ), 0L );
  409.                             }
  410.                                         
  411.                         if(( nChkErr = StreamBufferSetup()) != 0 )
  412.                             {
  413.                             // Error opening the WAVE file so abort
  414.                             break;
  415.                             }
  416.                         else
  417.                             {
  418.                             bFileOpen = TRUE;
  419.                             EnableMenuItem( GetMenu( hWnd ), IDM_PLAY,
  420.                                                 MF_BYCOMMAND | MF_ENABLED );
  421.                             EnableWindow( hWndPlay, TRUE );
  422.                             EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  423.                                                 MF_BYCOMMAND | MF_ENABLED );
  424.                             DrawMenuBar( hWnd );
  425.                             BuildTitleBarText();
  426.                             }
  427.                         }
  428.                     }
  429.                     break;
  430.  
  431.                 case IDM_FILE_CLOSE:
  432.                     SendMessage( hWnd, WM_COMMAND,
  433.                                 MAKEWPARAM( IDM_STOP,
  434.                                             DSSTREAM_STOPF_NOREOPEN ), 0L );
  435.                     BuildTitleBarText();
  436.                     break;
  437.  
  438.                 case IDM_OPTIONS_ENUMDRIVERS:
  439.                     bEnumDrivers = !bEnumDrivers;
  440.                     if( bEnumDrivers )
  441.                         {
  442.                         LoadString( hInst, IDS_ENUMWARNING, szTemp, sizeof(szTemp));
  443.                         MessageBox( hWnd, szTemp, szAppCaption, MB_OK );
  444.                         }
  445.                     break;
  446.  
  447.                 case IDM_HELP_ABOUT:
  448.                     DialogBox( hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndMain,
  449.                                 (DLGPROC)DLG_About );
  450.                     break;
  451.  
  452.                 case IDC_LOOPCHECK:
  453.                     wiWave.bLoopFile = !wiWave.bLoopFile;
  454.                     Button_SetCheck( hWndLoopCheck, wiWave.bLoopFile );
  455.                     if( !bPlaying && bFileOpen )
  456.                         ResetWavePlayer();
  457.                     break;
  458.  
  459.                 case IDM_PLAY:
  460. #ifdef DEBUG
  461.                     wsprintf( szDebug, "  bFileOpen = %s",
  462.                                 bFileOpen == TRUE ? "TRUE" : "FALSE" );
  463.                     DPF( 3, "IDM_PLAY Debug" );
  464.                     DPF( 3, szDebug );
  465.                     wsprintf( szDebug, "  bTimerInstalled = %s",
  466.                                 bTimerInstalled == TRUE ? "TRUE" : "FALSE" );
  467.                     DPF( 3, szDebug );
  468.                     wsprintf( szDebug, "  bPlaying = %s",
  469.                                 bPlaying == TRUE ? "TRUE" : "FALSE" );
  470.                     DPF( 3, szDebug );
  471. #endif
  472.                     if( bPlaying )
  473.                         SendMessage( hWnd, WM_COMMAND,
  474.                                         MAKEWPARAM( IDM_STOP, 0 ), 0L );
  475.                     if( bFileOpen && lpDSBStreamBuffer )
  476.                         {
  477.                         // Ensure that position is at 0, ready to go
  478.                         dsrval = lpDSBStreamBuffer->lpVtbl->SetCurrentPosition(
  479.                                 lpDSBStreamBuffer,
  480.                                 0 );
  481.                         dsrval = lpDSBStreamBuffer->lpVtbl->Play( lpDSBStreamBuffer,
  482.                                                       0, 0, DSBPLAY_LOOPING );
  483.                         }
  484.                     else
  485.                         {
  486.                         bPlaying = bTimerInstalled = FALSE;
  487.                         break;
  488.                         }
  489.  
  490.                     if( timeBeginPeriod( PLAYBACK_TIMER_PERIOD
  491.                                             / PLAYBACK_OVERSAMPLE ) != 0 )
  492.                         {
  493.                         /* Can't create timer! */
  494.                         dsrval = lpDSBStreamBuffer->lpVtbl->Stop( lpDSBStreamBuffer );
  495.                         bPlaying = bTimerInstalled = FALSE;
  496.                         break;
  497.                         }
  498.                     else
  499.                         {
  500.                             lInTimer = FALSE;
  501.                             if(( uTimerID = timeSetEvent( PLAYBACK_TIMER_PERIOD
  502.                                                            / PLAYBACK_OVERSAMPLE,
  503.                                                           PLAYBACK_TIMER_ACCURACY,
  504.                                                           TimeFunc, (DWORD)0,
  505.                                                           TIME_PERIODIC )) != 0 )
  506.                             bTimerInstalled = TRUE;
  507.                         }
  508.                     bPlaying = TRUE;
  509.                     EnableMenuItem( GetMenu( hWnd ), IDM_STOP,
  510.                                         MF_BYCOMMAND | MF_ENABLED );
  511.                     EnableWindow( hWndStop, TRUE );
  512.                     DrawMenuBar( hWnd );
  513.                     break;
  514.  
  515.                 case IDM_STOP:
  516. #ifdef DEBUG
  517.                     wsprintf( szDebug, "  bFileOpen = %s",
  518.                                     bFileOpen == TRUE ? "TRUE" : "FALSE" );
  519.                     DPF( 3, "IDM_STOP Debug" );
  520.                     DPF( 3, szDebug );
  521.                     wsprintf( szDebug, "  bTimerInstalled = %s",
  522.                                 bTimerInstalled == TRUE ? "TRUE" : "FALSE" );
  523.                     DPF( 3, szDebug );
  524.                     wsprintf( szDebug, "  bPlaying = %s",
  525.                                 bPlaying == TRUE ? "TRUE" : "FALSE" );
  526.                     DPF( 3, szDebug );
  527. #endif
  528.                     wiWave.bDonePlaying = TRUE;
  529.                     if( bTimerInstalled )
  530.                     {
  531.                         timeKillEvent( uTimerID );
  532.                         timeEndPeriod( PLAYBACK_TIMER_PERIOD / PLAYBACK_OVERSAMPLE );
  533.                         // Busy wait for timer func to exit
  534.                         while (InterlockedExchange(&lInTimer, TRUE)) Sleep(100);
  535.                         bTimerInstalled = FALSE;
  536.                     }
  537.                     if( bPlaying )
  538.                         {
  539.                         bPlaying = FALSE;
  540.                         dsrval = lpDSBStreamBuffer->lpVtbl->Stop( lpDSBStreamBuffer );
  541.                         EnableMenuItem( GetMenu( hWnd ), IDM_STOP,
  542.                                             MF_BYCOMMAND | MF_GRAYED );
  543.                         EnableWindow( hWndStop, FALSE );
  544.                         DrawMenuBar( hWnd );
  545.                         }
  546.                     if(!( HIWORD(wParam) & DSSTREAM_STOPF_NOREOPEN ))
  547.                         {
  548.                         ResetWavePlayer();
  549.                         break;
  550.                         }
  551.                     else
  552.                         {
  553.                         if( bFileOpen )
  554.                             {
  555.                             WaveCloseReadFile( &wiWave.hmmio,
  556.                                                             &wiWave.pwfx );
  557.                             if( lpDSBStreamBuffer )
  558.                                 dsrval = lpDSBStreamBuffer->lpVtbl->Release(
  559.                                                            lpDSBStreamBuffer );
  560.  
  561.                             lpDSBStreamBuffer = NULL;
  562.                             
  563.                             bFileOpen = FALSE;
  564.                             // The file is closed, so disable the close option
  565.                             EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  566.                                             MF_BYCOMMAND | MF_GRAYED );
  567.                             EnableMenuItem( GetMenu( hWnd ), IDM_PLAY,
  568.                                             MF_BYCOMMAND | MF_GRAYED );
  569.                             EnableWindow( hWndPlay, FALSE );
  570.                             }
  571.                         }
  572.                     break;
  573.  
  574.                 case IDM_FILE_EXIT:
  575.                     DestroyWindow( hWnd );
  576.                     break;
  577.                 }
  578.             break;
  579. #ifndef DEBUG
  580.         case WM_GETMINMAXINFO:
  581.     /*
  582.      * We know exactly how big this window should be, and it's sort of a
  583.      * little pop-up control panel, so we can disable window sizing by
  584.      * forcing all the minimum and maximum sizes to be the calculated size.
  585.      */
  586.             lpMinMax = (LPMINMAXINFO)lParam;
  587.  
  588.             lpMinMax->ptMaxSize.x = CONTROL_SPACE_CX + 2*BORDER_SPACE_CX
  589.                                     + BUTTON_CX + PAN_TB_CX + PAN_TEXT_CX
  590.                                     + TEXT_SPACE_CX
  591.                                     + 2*GetSystemMetrics( SM_CXBORDER );
  592.             lpMinMax->ptMaxSize.y = 2*(BORDER_SPACE_CY
  593.                                     + GetSystemMetrics( SM_CYBORDER ))
  594.                                     + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY
  595.                                     + PROG_TB_CY + 3*CONTROL_SPACE_CY
  596.                                     + GetSystemMetrics( SM_CYMENU )
  597.                                     + GetSystemMetrics( SM_CYCAPTION );
  598.  
  599.             lpMinMax->ptMinTrackSize.x = lpMinMax->ptMaxTrackSize.x
  600.                                                     = lpMinMax->ptMaxSize.x;
  601.  
  602.             lpMinMax->ptMinTrackSize.y = lpMinMax->ptMaxTrackSize.y
  603.                                                     = lpMinMax->ptMaxSize.y;
  604.             break;
  605. #endif
  606.         case WM_HSCROLL:
  607.             if(((HWND)lParam == hWndPan) && lpDSBStreamBuffer )
  608.                 {
  609.                 HandlePanScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  610.                 }
  611.             else if(((HWND)lParam == hWndVol) && lpDSBStreamBuffer )
  612.                 {
  613.                 HandleVolScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  614.                 }
  615.             else if(((HWND)lParam == hWndFreq) && lpDSBStreamBuffer )
  616.                 {
  617.                 HandleFreqScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  618.                 }
  619.             break;
  620.  
  621.         case WM_INITMENU:
  622.             if((HMENU)wParam != GetMenu( hWnd ))
  623.                 break;
  624.             CheckMenuItem((HMENU)wParam, IDM_OPTIONS_ENUMDRIVERS,
  625.                                 bEnumDrivers ? MF_CHECKED : MF_UNCHECKED );
  626.             break;
  627.  
  628.         case WM_DESTROY:
  629.     /*
  630.      * Free all the DirectSound objects we created
  631.      */
  632.             SendMessage( hWnd, WM_COMMAND,
  633.                           MAKEWPARAM( IDM_STOP, DSSTREAM_STOPF_NOREOPEN ), 0 );
  634.  
  635.             if( bTimerInstalled )
  636.             {
  637.                 timeKillEvent( uTimerID );
  638.                 timeEndPeriod( PLAYBACK_TIMER_PERIOD / PLAYBACK_OVERSAMPLE );
  639.                 // Busy wait for timer func to exit
  640.                 while (InterlockedExchange(&lInTimer, TRUE)) Sleep(100);
  641.                 bTimerInstalled = FALSE;
  642.             }
  643.  
  644.             if( lpDS ) dsrval = lpDS->lpVtbl->Release( lpDS );
  645.  
  646.             WriteProfileString( "DSSTREAM", "EnumDrivers",
  647.                                         bEnumDrivers ? "1" : "0" );
  648.  
  649.             PostQuitMessage( 0 );
  650.             break;
  651.  
  652.         default:
  653.             return DefWindowProc( hWnd, uMsg, wParam, lParam );
  654.         }
  655.     return 0L;
  656.     } /* WindowProc */
  657.  
  658.  
  659. /*****************************************************************************/
  660. /* DLG_About()                                                               */
  661. /*                                                                           */
  662. /*   Dialog procedure for the Help...About... box which simply pops up a     */
  663. /* little copyright message and brief program description.                   */
  664. /*                                                                           */
  665. /*****************************************************************************/
  666. BOOL CALLBACK DLG_About( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  667.     {
  668.     switch( msg )
  669.         {
  670.         case WM_INITDIALOG:
  671.             break;
  672.  
  673.         case WM_COMMAND:
  674.             switch( LOWORD(wParam))
  675.                 {
  676.                 case IDOK:
  677.                     EndDialog( hDlg, FALSE );
  678.                     return( TRUE );
  679.  
  680.                 default:
  681.                     break;
  682.                 }
  683.             break;
  684.  
  685.         default:
  686.             return( FALSE );
  687.         }
  688.  
  689.     return( FALSE );
  690.     }
  691.  
  692.  
  693. /*****************************************************************************/
  694. /* HandleCommDlgError()                                                      */
  695. /*                                                                           */
  696. /*    The function translates extended common dialog error codes into a      */
  697. /* string resource ID, loads that string from our module, and displays it in */
  698. /* a message box. This implementation only covers the general CD error codes.*/
  699. /*                                                                           */
  700. /*****************************************************************************/
  701. int HandleCommDlgError( DWORD dwError )
  702.     {
  703.     char szTitle[128];
  704.     UINT uMsgID;
  705.  
  706.     if( dwError == CDERR_DIALOGFAILURE )
  707.         uMsgID = IDS_CDERR_DIALOGFAILURE;
  708.     else
  709.         uMsgID = (UINT)dwError + IDS_CDERR_GENERAL_BASE;
  710.  
  711.     LoadString( hInst, uMsgID, szTemp, sizeof(szTemp));
  712.     LoadString( hInst, IDS_CDERR_TITLESTRING, szTitle, sizeof(szTitle));
  713.     MessageBox( GetActiveWindow(), szTemp, szTitle,
  714.                     MB_OK | MB_ICONEXCLAMATION );
  715.                 
  716.     return( 0 );
  717.     }
  718.  
  719.  
  720. /*****************************************************************************/
  721. /* StreamBufferSetup()                                                       */
  722. /*                                                                           */
  723. /*    This function uses the filename stored in the global character array to*/
  724. /* open a WAVE file. Then it creates a secondary DirectSoundBuffer object    */
  725. /* which will later be used to stream that file from disk during playback.   */
  726. /*                                                                           */
  727. /*****************************************************************************/
  728. int StreamBufferSetup( void )
  729.     {
  730.     DSBUFFERDESC dsbd;
  731.     HRESULT      dsRetVal;
  732.     LPBYTE       lpWrite1, lpWrite2;
  733.     DWORD        dwLen1, dwLen2;
  734.     UINT         uChkErr;
  735.         
  736.     int nChkErr;
  737.  
  738.     /* This portion of the WAVE I/O is patterned after what's in DSTRWAVE, which
  739.      * was in turn adopted from WAVE.C which is part of the DSSHOW sample.
  740.      */
  741.  
  742.     if(( nChkErr = WaveOpenFile( szFileBuffer, &wiWave.hmmio, &wiWave.pwfx, &wiWave.mmckInRIFF )) != 0 )
  743.         {
  744.         ErrorMessageBox( IDS_ERROR_WAVEFILEOPEN, MB_ICONEXCLAMATION );
  745.         return( ERR_WAVE_OPEN_FAILED );
  746.         }
  747.  
  748.     if( wiWave.pwfx->wFormatTag != WAVE_FORMAT_PCM )
  749.         {
  750.         ErrorMessageBox( IDS_ERROR_WAVENOTPCM, MB_ICONEXCLAMATION );
  751.         WaveCloseReadFile( &wiWave.hmmio, &wiWave.pwfx );
  752.         return( ERR_WAVE_INVALID_FORMAT );
  753.                 }
  754.     /* Seek to the data chunk */
  755.     if(( nChkErr = WaveStartDataRead( &wiWave.hmmio, &wiWave.mmck, &wiWave.mmckInRIFF )) != 0 )
  756.         {
  757.         ErrorMessageBox( IDS_ERROR_WAVESEEKFAILED, MB_ICONEXCLAMATION );
  758.         WaveCloseReadFile( &wiWave.hmmio, &wiWave.pwfx );
  759.         return( ERR_WAVE_CORRUPTED_FILE );
  760.         }
  761.     /* As a side note, mmck.ckSize will be the size of all the data in this file.
  762.      * That's something which might be handy when calculating the length... */
  763.  
  764.     /* Calculate a buffer length, making sure it is an exact multiple of the
  765.      * buffer segment size.
  766.      */
  767.     wiWave.dwBufferSize = ((DWORD)wiWave.pwfx->nAvgBytesPerSec
  768.                             * (((NUM_BUFFER_SEGMENTS * PLAYBACK_TIMER_PERIOD)
  769.                             / 10)) / 100);
  770.  
  771. #ifdef DEBUG
  772.     wsprintf( szDebug, "BufferSize = %lu", wiWave.dwBufferSize );
  773.     ListBox_AddString( hWndList, szDebug );
  774. #endif
  775.     /*
  776.      * Create the secondary DirectSoundBuffer object to receive our sound data.
  777.      */
  778.     memset( &dsbd, 0, sizeof( DSBUFFERDESC ));
  779.     dsbd.dwSize = sizeof( DSBUFFERDESC );
  780.     dsbd.dwFlags = DSBCAPS_CTRLDEFAULT;
  781.     dsbd.dwBufferBytes = wiWave.dwBufferSize;
  782.  
  783.     /* Set Format properties according to the WAVE file we just opened */
  784.     dsbd.lpwfxFormat = wiWave.pwfx;
  785.     dsRetVal = lpDS->lpVtbl->CreateSoundBuffer( lpDS,
  786.                                                 &dsbd,
  787.                                                 &lpDSBStreamBuffer,
  788.                                                 NULL );
  789.     if( dsRetVal != DS_OK )
  790.         {
  791.         ErrorMessageBox( IDS_ERROR_DSBCREATE, MB_ICONEXCLAMATION );
  792.         return( ERR_CREATEDSB_FAILED );
  793.         }
  794.  
  795.     wiWave.lpDSBStreamBuffer = lpDSBStreamBuffer;
  796.     wiWave.bFoundEnd = FALSE;
  797.     wiWave.dwBytesRemaining = 0;
  798.  
  799.     dsRetVal = lpDSBStreamBuffer->lpVtbl->Lock( lpDSBStreamBuffer,
  800.                                         0, wiWave.dwBufferSize,
  801.                                         &((LPVOID)lpWrite1), &dwLen1,
  802.                                         &((LPVOID)lpWrite2), &dwLen2,
  803.                                         0 );
  804.     if( dsRetVal != DS_OK )
  805.         {
  806.         //ErrorMessageBox( IDS_ERROR_DSBLOCK, MB_EXLCAMATION );
  807.         }
  808.  
  809.     if( dwLen1 )
  810.         {
  811.         nChkErr = WaveReadFile( wiWave.hmmio, (UINT)dwLen1, lpWrite1,
  812.                                 &wiWave.mmck, &uChkErr );
  813.         if( uChkErr < dwLen1 )
  814.             {
  815.             if( wiWave.bLoopFile )
  816.                 {
  817.     /* If the file is shorter than the buffer and we're looping, we need to
  818.      * read the file in again so that we don't get a block of silence before
  819.      * the timer loops playback.
  820.      */
  821.                 LPBYTE lpTemp = lpWrite1;
  822.  
  823.                 do
  824.                     {
  825.                     /* Continue decrementing our count and moving our temp
  826.                      * pointer forward until we've read the file enough times
  827.                      * to fill the buffer.  NOTE: It's probably not efficient
  828.                      * to bother with the overhead of streaming a file that's
  829.                      * not at least as large as the buffer... */
  830.                     lpTemp += uChkErr;
  831.                     dwLen1 -= uChkErr;
  832.                     nChkErr = WaveStartDataRead( &wiWave.hmmio,
  833.                                                     &wiWave.mmck,
  834.                                                     &wiWave.mmckInRIFF );
  835.                     nChkErr = WaveReadFile( wiWave.hmmio, (UINT)dwLen1,
  836.                                             lpTemp,
  837.                                             &wiWave.mmck, &uChkErr );
  838.                     } while( uChkErr < dwLen1 );
  839.                 }
  840.             else
  841.                 {
  842.                 wiWave.bFoundEnd = TRUE;
  843.                 wiWave.dwBytesRemaining = (DWORD)uChkErr;
  844.                 DPF( 3,"Setting bFoundEnd in load" );
  845.                 _fmemset( (lpWrite1+uChkErr),
  846.                                 wiWave.pwfx->wBitsPerSample == 8 ? 128 : 0,
  847.                                 (dwLen1 - uChkErr));
  848.                 }
  849.             }
  850.         }
  851.     dsRetVal = lpDSBStreamBuffer->lpVtbl->Unlock( lpDSBStreamBuffer,
  852.                                         (LPVOID)lpWrite1, dwLen1,
  853.                                         (LPVOID)lpWrite2, 0 );
  854.     wiWave.dwNextWriteOffset = wiWave.dwProgress = 0;
  855.     wiWave.bDonePlaying = FALSE;
  856.     wiWave.bLoopFile = Button_GetCheck( hWndLoopCheck );
  857.  
  858. #ifdef DEBUG
  859.     wsprintf( szDebug, "wiWave.dwBufferSize = %lu", wiWave.dwBufferSize );
  860.     DPF( 3, "StreamBufferSetup Debug" );
  861.     DPF( 3, szDebug );
  862. #endif
  863.  
  864.     SendMessage( hWndVol, TBM_SETPOS, TRUE, VOL_MAX );
  865.     SendMessage( hWndPan, TBM_SETPOS, TRUE, PAN_CENTER );
  866.     SendMessage( hWndFreq, TBM_SETPOS, TRUE,
  867.                     (LPARAM)wiWave.pwfx->nSamplesPerSec / FREQ_MULTIPLIER );
  868.     SendMessage( hWndMain, WM_DSSTREAM_PROGRESS, 0L, 0L );
  869.     UpdateFromControls();
  870.     return( 0 );
  871.     }
  872.  
  873.  
  874. /*****************************************************************************/
  875. /* ResetWavePlayer()                                                         */
  876. /*                                                                           */
  877. /*  Performs a subset of the above operations (in StreamBufferSetup). Things */
  878. /* not done include creating a DSB and opening the file (it's already open). */
  879. /*                                                                           */
  880. /*****************************************************************************/
  881. void ResetWavePlayer( void )
  882.     {
  883.     LPBYTE      lpWrite1, lpWrite2;
  884.     DWORD       dwLen1, dwLen2;
  885.     UINT        uChkErr;
  886.     int         nChkErr;
  887.     HRESULT     dsrval;
  888.  
  889.     WaveStartDataRead( &wiWave.hmmio, &wiWave.mmck, &wiWave.mmckInRIFF );
  890.     wiWave.bFoundEnd = FALSE;
  891.     wiWave.dwBytesRemaining = 0;
  892.  
  893.     dsrval = lpDSBStreamBuffer->lpVtbl->Lock( lpDSBStreamBuffer,
  894.                                         0, wiWave.dwBufferSize,
  895.                                         &((LPVOID)lpWrite1), &dwLen1,
  896.                                         &((LPVOID)lpWrite2), &dwLen2,
  897.                                         0 );
  898.  
  899.     if( dwLen1 )
  900.         {
  901.         nChkErr = WaveReadFile( wiWave.hmmio, (UINT)dwLen1, lpWrite1,
  902.                                 &wiWave.mmck, &uChkErr );
  903.         if( uChkErr < dwLen1 )
  904.             {
  905.             if( wiWave.bLoopFile )
  906.                 {
  907.     /* If the file is shorter than the buffer and we're looping, we need to
  908.      * read the file in again so that we don't get a block of silence before
  909.      * the timer loops playback.
  910.      */
  911.                 LPBYTE lpTemp = lpWrite1;
  912.  
  913.                 do
  914.                     {
  915.                     /* Continue decrementing our count and moving our temp
  916.                      * pointer forward until we've read the file enough times
  917.                      * to fill the buffer.  NOTE: It's probably not efficient
  918.                      * to bother with the overhead of streaming a file that's
  919.                      * not at least as large as the buffer... */
  920.                     lpTemp += uChkErr;
  921.                     dwLen1 -= uChkErr;
  922.                     nChkErr = WaveStartDataRead( &wiWave.hmmio,
  923.                                                     &wiWave.mmck,
  924.                                                     &wiWave.mmckInRIFF );
  925.                     nChkErr = WaveReadFile( wiWave.hmmio, (UINT)dwLen1,
  926.                                             lpTemp,
  927.                                             &wiWave.mmck, &uChkErr );
  928.                     } while( uChkErr < dwLen1 );
  929.                 }
  930.             else
  931.                 {
  932.                 wiWave.bFoundEnd = TRUE;
  933.                 wiWave.dwBytesRemaining = (DWORD)uChkErr;
  934.                 DPF( 3,"Setting bFoundEnd in load, dwBytesRemaining = %ul",
  935.                         wiWave.dwBytesRemaining );
  936.  
  937.                 // Cover ourselves by filling the rest of the buffer space
  938.                 // with 8 or 16 bit silence, in case we can't stop the playback
  939.                 // exactly on a block boundary and we run a bit into NULL data
  940.                 _fmemset(( lpWrite1 + uChkErr),
  941.                                 wiWave.pwfx->wBitsPerSample == 8 ? 128 : 0,
  942.                                 dwLen1 - uChkErr);
  943.                 }
  944.             }
  945.         }
  946.     dsrval = lpDSBStreamBuffer->lpVtbl->Unlock( lpDSBStreamBuffer,
  947.                                         (LPVOID)lpWrite1, dwLen1,
  948.                                         (LPVOID)lpWrite2, 0 );
  949.     wiWave.dwNextWriteOffset = wiWave.dwProgress = 0;
  950.     wiWave.bDonePlaying = FALSE;
  951.     SendMessage( hWndMain, WM_DSSTREAM_PROGRESS, 0L, 0L );
  952.     }
  953.  
  954.  
  955. /*****************************************************************************/
  956. /* CreateChildren()                                                          */
  957. /*                                                                           */
  958. /*   This function creates a bunch of child controls for the main window.    */
  959. /* Most of them are used for controling various things about a playing sound */
  960. /* file, like volume and panning. Returns FALSE if no errors, TRUE otherwise.*/
  961. /*                                                                           */
  962. /*****************************************************************************/
  963. int CreateChildren( RECT crect )
  964.     {
  965.     SIZE  Size;
  966.     HDC   hDC;
  967.     int   x, y;
  968.     UINT  uType;
  969.     char  szTemplate[128], szType[32];
  970.     LPSTR lpszControl;
  971.  
  972.     LoadString( hInst, IDS_ERROR_CHILDTEMPLATE, szTemplate, sizeof(szTemplate));
  973.  
  974.     /* Don't handle failure for this one, because the app will still run fine */
  975.     hWndBar = CreateWindow( "static", NULL,
  976.                             WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
  977.                             0, 0, crect.right, 2, hWndMain,
  978.                             (HMENU)0, hInst, NULL );
  979.  
  980.     hDC = GetDC( hWndMain );
  981.     if( !GetTextExtentPoint32( hDC, szPan, strlen(szPan), &Size ))
  982.         {
  983.         ErrorMessageBox( IDS_ERROR_GETTEXTEXTENT, MB_ICONEXCLAMATION );
  984.         ReleaseDC( hWndMain, hDC );
  985.         return( TRUE );
  986.         }
  987.     ReleaseDC( hWndMain, hDC );
  988.  
  989.     y = BORDER_SPACE_CY;
  990.  
  991.     /* STATIC control -- text label for the pan trackbar */
  992.     if(( hWndPanText = CreateWindow( "static", szPan,
  993.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  994.                                     BORDER_SPACE_CX + PAN_TB_CX + TEXT_SPACE_CX,
  995.                                     y + (PAN_TB_CY - Size.cy)/2,
  996.                                     PAN_TEXT_CX, Size.cy,
  997.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  998.         {
  999.         lpszControl = szPan;
  1000.         uType = IDS_ERROR_STATICTEXT;
  1001.         goto DISPLAY_CREATE_FAILURE;
  1002.         }
  1003.  
  1004.     /* PAN (left to right balance) trackbar control */
  1005.     if(( hWndPan = CreateWindow( TRACKBAR_CLASS, NULL,
  1006.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1007.                                 BORDER_SPACE_CX,
  1008.                                 y, PAN_TB_CX, PAN_TB_CY,
  1009.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1010.         {
  1011.         lpszControl = szPan;
  1012.         uType = IDS_ERROR_TRACKBAR;
  1013.         goto DISPLAY_CREATE_FAILURE;
  1014.         }
  1015.  
  1016.     SendMessage( hWndPan, TBM_SETRANGE, FALSE, MAKELONG( PAN_MIN, PAN_MAX )); 
  1017.     SendMessage( hWndPan, TBM_SETPOS, TRUE, PAN_CENTER );
  1018.     SendMessage( hWndPan, TBM_SETPAGESIZE, 0L, PAN_PAGESIZE );
  1019.  
  1020.     y += PAN_TB_CY + CONTROL_SPACE_CY;
  1021.  
  1022.     /* STATIC control -- text label for the volume trackbar */
  1023.     if(( hWndVolText = CreateWindow( "static", szVolume,
  1024.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1025.                                     BORDER_SPACE_CX + VOL_TB_CX + TEXT_SPACE_CX,
  1026.                                     y + (VOL_TB_CY - Size.cy)/2,
  1027.                                     VOL_TEXT_CX, Size.cy,
  1028.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1029.         {
  1030.         lpszControl = szVolume;
  1031.         uType = IDS_ERROR_STATICTEXT;
  1032.         goto DISPLAY_CREATE_FAILURE;
  1033.         }
  1034.  
  1035.     /* Create the VOLUME trackbar */
  1036.     if(( hWndVol = CreateWindow( TRACKBAR_CLASS, NULL,
  1037.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1038.                                 BORDER_SPACE_CX,
  1039.                                 y, VOL_TB_CX, VOL_TB_CY,
  1040.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1041.         {
  1042.         lpszControl = szVolume;
  1043.         uType = IDS_ERROR_TRACKBAR;
  1044.         goto DISPLAY_CREATE_FAILURE;
  1045.         }
  1046.  
  1047.     SendMessage( hWndVol, TBM_SETRANGE, FALSE,
  1048.                                         MAKELONG( VOL_MIN, VOL_MAX ));
  1049.     SendMessage( hWndVol, TBM_SETPOS, TRUE, VOL_MAX );
  1050.     SendMessage( hWndVol, TBM_SETPAGESIZE, 0L, VOL_PAGESIZE );
  1051.  
  1052.     y += VOL_TB_CY + CONTROL_SPACE_CY;
  1053.  
  1054.     /* STATIC control -- text label for the frequency trackbar */
  1055.     if(( hWndFreqText = CreateWindow( "static", szFrequency,
  1056.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1057.                                     BORDER_SPACE_CX + FREQ_TB_CX + TEXT_SPACE_CX,
  1058.                                     y + (FREQ_TB_CY - Size.cy)/2,
  1059.                                     FREQ_TEXT_CX, Size.cy,
  1060.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1061.         {
  1062.         lpszControl = szFrequency;
  1063.         uType = IDS_ERROR_STATICTEXT;
  1064.         goto DISPLAY_CREATE_FAILURE;
  1065.         }
  1066.  
  1067.     /* Create the FREQUENCY trackbar */
  1068.     if(( hWndFreq = CreateWindow( TRACKBAR_CLASS, NULL,
  1069.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1070.                                 BORDER_SPACE_CX,
  1071.                                 y, FREQ_TB_CX, FREQ_TB_CY,
  1072.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1073.         {
  1074.         lpszControl = szFrequency;
  1075.         uType = IDS_ERROR_TRACKBAR;
  1076.         goto DISPLAY_CREATE_FAILURE;
  1077.         }
  1078.  
  1079.     SendMessage( hWndFreq, TBM_SETRANGE, FALSE, MAKELONG( FREQ_MIN, FREQ_MAX ));
  1080.     SendMessage( hWndFreq, TBM_SETPOS, TRUE, FREQ_MAX );
  1081.     SendMessage( hWndFreq, TBM_SETPAGESIZE, 0L, FREQ_PAGESIZE );
  1082.  
  1083.     y += FREQ_TB_CY + CONTROL_SPACE_CY;
  1084.  
  1085.     /* STATIC control -- text label for the progress trackbar */
  1086.     if(( hWndProgText = CreateWindow( "static", szProgress,
  1087.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1088.                                     BORDER_SPACE_CX + PROG_TB_CX + TEXT_SPACE_CX,
  1089.                                     y + (PROG_TB_CY - Size.cy)/2,
  1090.                                     PROG_TEXT_CX, Size.cy,
  1091.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1092.         {
  1093.         lpszControl = szProgress;
  1094.         uType = IDS_ERROR_STATICTEXT;
  1095.         goto DISPLAY_CREATE_FAILURE;
  1096.         }
  1097.  
  1098.     /* Create the PROGRESSS trackbar */
  1099.     if(( hWndProg = CreateWindow( TRACKBAR_CLASS, NULL,
  1100.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ
  1101.                                 | TBS_BOTTOM | WS_DISABLED,
  1102.                                 BORDER_SPACE_CX,
  1103.                                 y, PROG_TB_CX, PROG_TB_CY,
  1104.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1105.         {
  1106.         lpszControl = szProgress;
  1107.         uType = IDS_ERROR_TRACKBAR;
  1108.         goto DISPLAY_CREATE_FAILURE;
  1109.         }
  1110.  
  1111.     SendMessage( hWndProg, TBM_SETRANGE,
  1112.                         FALSE, MAKELPARAM( PROG_MIN, PROG_MAX ));
  1113.     SendMessage( hWndProg, TBM_SETPOS, TRUE, 0L );
  1114.  
  1115.     x = BORDER_SPACE_CX + PAN_TEXT_CX + TEXT_SPACE_CX
  1116.                 + PAN_TB_CX + CONTROL_SPACE_CX;
  1117.     y += PROG_TB_CY;
  1118.     y -= 2*(BUTTON_CY + BUTTON_SPACE_CY) + CHECK_CY;
  1119.  
  1120.     /* Create the LOOPED CHECKBOX */
  1121.     LoadString( hInst, IDS_CHECK_LOOPED, szTemp, sizeof(szTemp));
  1122.     if(( hWndLoopCheck = CreateWindow( "button", szTemp,
  1123.                                 WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
  1124.                                 x, y, CHECK_CX, CHECK_CY, hWndMain,
  1125.                                 (HMENU)IDC_LOOPCHECK, hInst, NULL )) == NULL )
  1126.         {
  1127.         lpszControl = szTemp;
  1128.         uType = IDS_ERROR_CHECK;
  1129.         goto DISPLAY_CREATE_FAILURE;
  1130.         }
  1131.     y += CHECK_CY + BUTTON_SPACE_CY;
  1132.  
  1133.     /* Create the PLAY BUTTON */
  1134.     LoadString( hInst, IDS_BUTTON_PLAY, szTemp, sizeof(szTemp));
  1135.     if(( hWndPlay = CreateWindow( "button", szTemp,
  1136.                                     WS_CHILD | WS_VISIBLE | WS_DISABLED,
  1137.                                     x, y, BUTTON_CX, BUTTON_CY, hWndMain,
  1138.                                     (HMENU)IDM_PLAY, hInst, NULL )) == NULL )
  1139.         {
  1140.         lpszControl = szTemp;
  1141.         uType = IDS_ERROR_BUTTON;
  1142.         goto DISPLAY_CREATE_FAILURE;
  1143.         }
  1144.     y += BUTTON_CY + BUTTON_SPACE_CY;
  1145.  
  1146.     /* Create the STOP BUTTON */
  1147.     LoadString( hInst, IDS_BUTTON_STOP, szTemp, sizeof(szTemp));
  1148.     if(( hWndStop = CreateWindow( "button", szTemp,
  1149.                                     WS_CHILD | WS_VISIBLE | WS_DISABLED,
  1150.                                     x, y, BUTTON_CX, BUTTON_CY, hWndMain,
  1151.                                     (HMENU)IDM_STOP, hInst, NULL )) == NULL )
  1152.         {
  1153.         lpszControl = szTemp;
  1154.         uType = IDS_ERROR_BUTTON;
  1155.         goto DISPLAY_CREATE_FAILURE;
  1156.         }
  1157.  
  1158.     UpdateFromControls();
  1159.     goto RETURN_NORMAL;
  1160.  
  1161. DISPLAY_CREATE_FAILURE:
  1162.     LoadString( hInst, uType, szType, sizeof(szType));
  1163.     wsprintf( szTemp, szTemplate, lpszControl, szType );
  1164.     MessageBox( GetActiveWindow(), szTemp,
  1165.                         szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  1166.     return( TRUE );
  1167.  
  1168. RETURN_NORMAL:
  1169.     return( FALSE );
  1170.     }
  1171.  
  1172.  
  1173. /********************************************************************************/
  1174. /* UpdateFromControls()                                                         */
  1175. /*                                                                              */
  1176. /*    This function gets all the required values from the DirectSoundBuffer and */
  1177. /* updates the screen interface controls.                                       */
  1178. /*                                                                              */
  1179. /********************************************************************************/
  1180. void UpdateFromControls( void )
  1181.     {
  1182.     long        lPan, lVol, lFreq;
  1183.     HRESULT hr;
  1184.  
  1185.     lPan = (LONG)SendMessage( hWndPan, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1186.     lVol = (LONG)SendMessage( hWndVol, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1187.     lFreq = (LONG)SendMessage( hWndFreq, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1188.  
  1189.     /* Set the volume and then the pan */
  1190.     if( lpDSBStreamBuffer )
  1191.         {
  1192.         /* Set the volume */
  1193.         wsprintf( szTemp, "%s: %lidB", szVolume,
  1194.                                             ( lVol + VOL_SHIFT ) / VOL_DIV );
  1195.         Static_SetText( hWndVolText, szTemp );
  1196.  
  1197.         hr = lpDSBStreamBuffer->lpVtbl->SetVolume( lpDSBStreamBuffer,
  1198.                                             (((lVol+VOL_SHIFT) * VOL_MULT)) );
  1199.         if( hr != 0 )
  1200.             DPF( 0, "Unable to SetVolume in UpdateFromControls()" );
  1201.         else
  1202.             {
  1203.             wsprintf( szDebug, "Set volume to %lidB",
  1204.                                             ( lVol + VOL_SHIFT ) / VOL_DIV );
  1205.             DPF( 3, szDebug );
  1206.             }
  1207.  
  1208.         /* Set the Pan */
  1209.         wsprintf( szTemp, "%s: %lidB", szPan, ( lPan + PAN_SHIFT ) / PAN_DIV );
  1210.         Static_SetText( hWndPanText, szTemp );
  1211.  
  1212.         hr = lpDSBStreamBuffer->lpVtbl->SetPan( lpDSBStreamBuffer,
  1213.                                             (((lPan+PAN_SHIFT) * PAN_MULT)) );
  1214.         if( hr != 0 )
  1215.             DPF( 0, "Unable to SetPan in UpdateFromControls()" );
  1216.         else
  1217.             {
  1218.             wsprintf( szDebug, "Set pan to %lidB",
  1219.                                             ( lPan + PAN_SHIFT ) / PAN_DIV );
  1220.             DPF( 3, szDebug );
  1221.             }
  1222.  
  1223.         /* Set the frequency */
  1224.         wsprintf( szTemp, "%s: %liHz", szFrequency, lFreq * FREQ_MULTIPLIER );
  1225.         Static_SetText( hWndFreqText, szTemp );
  1226.  
  1227.         hr = lpDSBStreamBuffer->lpVtbl->SetFrequency( lpDSBStreamBuffer,
  1228.                                                         lFreq * FREQ_MULTIPLIER);
  1229.         if( hr != 0 )
  1230.             DPF( 0, "Unable to SetFrequency in UpdateFromControls()" );
  1231.         else
  1232.             {
  1233.             wsprintf( szDebug, "Set frequency to %liHz", lFreq*FREQ_MULTIPLIER );
  1234.             DPF( 3, szDebug );
  1235.             }
  1236.         }
  1237.         return;
  1238.     }
  1239.  
  1240.  
  1241. /********************************************************************************/
  1242. /* HandlePanScroll()                                                            */
  1243. /*                                                                              */
  1244. /*   Handles the pan trackbar scroll when a WM_HSCROLL is received.             */
  1245. /*                                                                              */
  1246. /********************************************************************************/
  1247. void HandlePanScroll( int nCode, int nPos )
  1248.     {
  1249.     long  lPan, lDelta;
  1250.  
  1251.     lPan = (LONG)SendMessage( hWndPan, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1252.  
  1253.     switch( nCode )
  1254.         {
  1255.         case TB_LINEUP:
  1256.             if( lPan >= PAN_MIN - 1 )
  1257.                 lDelta = -1;
  1258.             break;
  1259.         case TB_LINEDOWN:
  1260.             if( lPan <= PAN_MAX + 1 )
  1261.                 lDelta = 1;
  1262.             break;
  1263.         case TB_PAGEUP:
  1264.             if( lPan >= PAN_MIN - PAN_PAGESIZE )
  1265.                 lDelta = -16;
  1266.             break;
  1267.         case TB_PAGEDOWN:
  1268.             if( lPan <= PAN_MAX + PAN_PAGESIZE )
  1269.                 lDelta = 16;
  1270.             break;
  1271.         case TB_ENDTRACK:
  1272.             return;
  1273.         default:
  1274.             lDelta = 0;
  1275.         }
  1276.  
  1277.     if( lDelta )
  1278.         SendMessage( hWndPan, TBM_SETPOS, TRUE, lPan + lDelta );
  1279.     else
  1280.         SendMessage( hWndPan, TBM_SETPOS, TRUE, (long)nPos );
  1281.     UpdateFromControls();
  1282.     }
  1283.  
  1284.  
  1285. /********************************************************************************/
  1286. /* HandleVolScroll()                                                            */
  1287. /*                                                                              */
  1288. /*   Handles the volume trackbar scrolling when a WM_HSCROLL is received.       */
  1289. /*                                                                              */
  1290. /********************************************************************************/
  1291. void HandleVolScroll( int nCode, int nPos )
  1292.     {
  1293.     long  lVol, lDelta;
  1294.  
  1295.     lVol = (LONG)SendMessage( hWndVol, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1296.  
  1297.     switch( nCode )
  1298.         {
  1299.         case TB_LINEDOWN:
  1300.             if( lVol <= VOL_MAX - 1 )
  1301.                 lDelta = 1;
  1302.             break;
  1303.         case TB_LINEUP:
  1304.             if( lVol >= VOL_MIN + 1 )
  1305.                 lDelta = -1;
  1306.             break;
  1307.         case TB_PAGEDOWN:
  1308.             if( lVol <= VOL_MAX - VOL_PAGESIZE )
  1309.                 lDelta = 10;
  1310.             break;
  1311.         case TB_PAGEUP:
  1312.             if( lVol >= VOL_MIN + VOL_PAGESIZE )
  1313.                 lDelta = -10;
  1314.             break;
  1315.         case TB_ENDTRACK:
  1316.             return;
  1317.         default:
  1318.             lDelta = 0;
  1319.         }
  1320.  
  1321.     if( lDelta )
  1322.         SendMessage( hWndVol, TBM_SETPOS, TRUE, (lVol + lDelta));
  1323.     else
  1324.         SendMessage( hWndVol, TBM_SETPOS, TRUE, (long)nPos );
  1325.     UpdateFromControls();
  1326.     }
  1327.  
  1328.  
  1329. /********************************************************************************/
  1330. /* HandleFreqScroll()                                                           */
  1331. /*                                                                              */
  1332. /*   Handles the volume trackbar scrolling when a WM_HSCROLL is received.       */
  1333. /*                                                                              */
  1334. /********************************************************************************/
  1335. void HandleFreqScroll( int nCode, int nPos )
  1336.     {
  1337.     long  lFreq, lDelta;
  1338.  
  1339.     lFreq = (LONG)SendMessage( hWndFreq, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1340.  
  1341.     switch( nCode )
  1342.         {
  1343.         case TB_LINEDOWN:
  1344.             if( lFreq <= FREQ_MAX-1 )
  1345.                 lDelta = 1;
  1346.             break;
  1347.         case TB_LINEUP:
  1348.             if( lFreq >= FREQ_MIN+1 )
  1349.                 lDelta = -1;
  1350.             break;
  1351.         case TB_PAGEDOWN:
  1352.             if( lFreq <= FREQ_MAX - FREQ_PAGESIZE )
  1353.                 lDelta = 10;
  1354.             break;
  1355.         case TB_PAGEUP:
  1356.             if( lFreq >= FREQ_MIN + FREQ_PAGESIZE )
  1357.                 lDelta = -10;
  1358.             break;
  1359.         case TB_ENDTRACK:
  1360.             return;
  1361.         default:
  1362.             lDelta = 0;
  1363.         }
  1364.  
  1365.     if( lDelta )
  1366.         SendMessage( hWndFreq, TBM_SETPOS, TRUE, (lFreq + lDelta));
  1367.     else
  1368.         SendMessage( hWndFreq, TBM_SETPOS, TRUE, (long)nPos );
  1369.     UpdateFromControls();
  1370.     }
  1371.  
  1372.  
  1373. /****************************************************************************/
  1374. /* ErrorMessageBox()                                                        */
  1375. /*                                                                          */
  1376. /*   A little routine to load error messages from the string resource table */
  1377. /* and pop them up in a MessageBox() for the world to see. The dwMBFlags    */
  1378. /* parameter allows the caller to specify the type of icon to use.          */
  1379. /*                                                                          */
  1380. /****************************************************************************/
  1381. void ErrorMessageBox( UINT uID, DWORD dwMBFlags )
  1382.     {
  1383.     LoadString( hInst, uID, szTemp, sizeof(szTemp));
  1384.     MessageBox( GetActiveWindow(), szTemp, szAppTitle, MB_OK | dwMBFlags );
  1385.     }
  1386.  
  1387.  
  1388. /****************************************************************************/
  1389. /* BuildTitleBar()                                                          */
  1390. /*                                                                          */
  1391. /*   Small routine to build and set the title bar text depending on whether */
  1392. /* or not a file is open.                                                   */
  1393. /****************************************************************************/
  1394. void BuildTitleBarText( void )
  1395.     {
  1396.     char szTitle[ sizeof(szAppCaption) + MAX_PATH + sizeof(" - ")];
  1397.  
  1398.     lstrcpy( szTitle, szAppCaption );
  1399.     if( bFileOpen )
  1400.         {
  1401.         lstrcat( szTitle, " - " );
  1402.         lstrcat( szTitle, szFileTitle );
  1403.         }
  1404.     SetWindowText( hWndMain, szTitle );
  1405.     }
  1406.  
  1407.