home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / dsstream / dsstream.c < prev    next >
C/C++ Source or Header  |  1997-07-14  |  62KB  |  1,672 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1997 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. #define INITGUID
  12. #include <windows.h>
  13. #include <windowsx.h>
  14. #include <mmsystem.h>
  15. #include <dsound.h>
  16. #include <commctrl.h>
  17. #include <commdlg.h>
  18. #include <memory.h>
  19. #include <cderr.h>
  20.  
  21. #include "dsstream.h"
  22. #include "wassert.h"
  23.  
  24. char szAppClass[] = "DSStreamWndClass";
  25. char szAppName[]  = "DSStream";
  26.  
  27. char szAppTitle[64];
  28. char szAppCaption[64];
  29. char szPan[32];
  30. char szVolume[32];
  31. char szFrequency[32];
  32. char szProgress[32];
  33. char szOpenFilter[128];
  34. char szOpenDLGTitle[64];
  35.  
  36.  
  37. char szTemp[256];
  38. char szDebug[128];
  39. char szFileBuffer[MAX_PATH];
  40. char szFileTitle[MAX_PATH];
  41. char szCDStartPath[MAX_PATH];            // The path to start the Open dialog in
  42.  
  43. // Registry Key and Value names that allow us to retrive a path something like
  44. // "C:\DXSDK\SDK\MEDIA", but matching the current install.
  45. static const TCHAR gszRegKeyDirect3D[] = TEXT("Software\\Microsoft\\Direct3D");
  46. static const TCHAR gszRegValueD3DPath[] = TEXT("D3D Path");
  47.  
  48. // Size of longest token below
  49. #define MAX_TOKEN_LEN    7
  50.  
  51.  
  52. static TCHAR gszPlayToken[] = TEXT("PLAY");
  53. static TCHAR gszLoopToken[] = TEXT("LOOP");
  54. static TCHAR gszCloseToken[] = TEXT("CLOSE");
  55. static TCHAR gszStickyToken[] = TEXT("STICKY");
  56. static TCHAR gszGlobalToken[] = TEXT("GLOBAL");
  57.  
  58. LPDIRECTSOUND        lpDS = NULL;
  59. LPDIRECTSOUNDBUFFER    lpDSBStreamBuffer = NULL;
  60. LPDIRECTSOUNDNOTIFY lpDirectSoundNotify = NULL;
  61.  
  62. WAVEINFOCA        wiWave;
  63.  
  64. HWND    hWndMain, hWndPan, hWndPanText, hWndVol, hWndVolText, hWndFreqText;
  65. HWND    hWndBar, hWndPlay, hWndStop, hWndLoopCheck, hWndFreq, hWndProg;
  66. HWND    hWndProgText;
  67.  
  68. #ifdef DEBUG
  69. HWND            hWndList;
  70. #endif
  71.  
  72. HINSTANCE    hInst;
  73.  
  74. static BOOL    bFileOpen = FALSE, bPlaying = FALSE;
  75. static BOOL     bEnumDrivers = FALSE, gfCloseOnDone = FALSE;
  76. static UINT     uTimerID = 0, uLastPercent = 100;
  77. static GUID     guID;
  78. static DWORD    gdwFocus = 0;
  79.  
  80. static BOOL InitApp( HINSTANCE );
  81. static BOOL InitInstance( HINSTANCE, int );
  82. static void BuildTitleBarText( void );
  83. // Call this in initialization to set the szCDStartPath[] global variable
  84. static void GetMediaStartPath( void );
  85. static void FillDataBuffer( void );
  86. static void LoadFromCommandLine( LPSTR lpszCmd );
  87. extern void UpdateProgressBar(void);
  88. /******************************************************************************
  89.  * WinMain()
  90.  *
  91.  * Entry point for all Windows programs - performs initialization, message loop
  92.  */
  93. int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  94.                     LPSTR lpCmdLine, int nCmdShow )
  95.     {
  96.     MSG     msg;
  97.  
  98.     hInst = hInstance;
  99.  
  100.     /* Make sure the common controls are loaded for our use */
  101.     InitCommonControls();
  102.  
  103.  
  104.     if( !hPrevInstance )
  105.         if( !InitApp( hInstance ))
  106.             {
  107.             ErrorMessageBox( IDS_ERROR_APPINIT, MB_ICONSTOP );
  108.             return( FALSE );
  109.         }
  110.  
  111.     if( !InitInstance( hInstance, nCmdShow ))
  112.         {
  113.         ErrorMessageBox( IDS_ERROR_INSTANCEINIT, MB_ICONSTOP );
  114.         return( FALSE );
  115.     }
  116.  
  117.     // We know how to take exactly one file name from the command-line and open
  118.     // it as a file to play.  We can also accept a couple command like /play,
  119.     // /close, and /loop
  120.     if( lpCmdLine[0] )
  121.     LoadFromCommandLine( lpCmdLine );
  122.  
  123.     while( GetMessage((LPMSG)&msg, NULL, 0, 0 ))
  124.         {
  125.         TranslateMessage( &msg );
  126.         DispatchMessage( &msg );
  127.         }
  128.  
  129.     UnregisterClass( szAppClass, hInstance );
  130.     return( msg.wParam );
  131.     } /* End of WinMain() */
  132.  
  133.  
  134. /*****************************************************************************/
  135. /* InitApp()                                     */
  136. /*                                          */
  137. /*   Inits things that only need to be created once for the this application */
  138. /* (like creating the window class).                         */
  139. /*****************************************************************************/
  140. static BOOL InitApp( HINSTANCE hInstance )
  141.     {
  142.     WNDCLASS    wc;
  143.  
  144.     /* Set up and register a window class */
  145.     wc.style        = CS_HREDRAW | CS_VREDRAW;
  146.     wc.lpszClassName    = szAppClass;
  147.     wc.lpfnWndProc    = (WNDPROC)MainWindowProc;
  148.     wc.cbClsExtra    = 0;
  149.     wc.cbWndExtra    = sizeof( DWORD );
  150.     wc.hInstance    = hInstance;
  151.     wc.hIcon            = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON1 ));
  152.     wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
  153.     wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW);
  154.     wc.lpszMenuName    = MAKEINTRESOURCE( IDR_MAINMENU );
  155.  
  156.     if( !RegisterClass( &wc ))
  157.         {
  158.     ErrorMessageBox( IDS_ERROR_REGISTERCLASS, MB_ICONSTOP );
  159.         return( FALSE );
  160.         }
  161.     return( TRUE );
  162.     } /* End of InitApp() */
  163.  
  164.  
  165. /*****************************************************************************/
  166. /* InitInstance()                                 */
  167. /*                                          */
  168. /* Performs initialization that must be done for each application instance.  */
  169. /*                                          */
  170. /*****************************************************************************/
  171. static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
  172.     {
  173.     HWND    hWnd;
  174.     HRESULT    dsRetVal;
  175.     RECT    crect;
  176.     int        cx, cy;
  177.     UINT    uCharsRead;
  178.     BOOL    fUseGuid = FALSE;
  179.     HMENU    hSysMenu;
  180.  
  181.     DbgInitialize( TRUE );
  182.  
  183.     // Initialize the Media start path for common open boxes
  184.     GetMediaStartPath();
  185.  
  186.     LoadString( hInstance, IDS_APP_TITLE, szAppTitle, sizeof(szAppTitle));
  187.     LoadString( hInstance, IDS_APP_CAPTION, szAppCaption, sizeof(szAppCaption));
  188.     LoadString( hInstance, IDS_TBTITLE_PAN, szPan, sizeof(szPan));
  189.     LoadString( hInstance, IDS_TBTITLE_VOLUME, szVolume, sizeof(szVolume));
  190.     LoadString( hInstance, IDS_TBTITLE_FREQUENCY,
  191.                             szFrequency, sizeof(szFrequency));
  192.     LoadString( hInstance, IDS_TBTITLE_PROGRESS,
  193.                             szProgress, sizeof(szProgress));
  194.     LoadString( hInstance, IDS_OPEN_DLGTITLE,
  195.                         szOpenDLGTitle, sizeof(szOpenDLGTitle));
  196. /* This is a little trick designed to allow us to load a common dialog box
  197.  * filter string, which is really a concatentation of several NULL-terminated
  198.  * strings. Note that while is is possible to enter something else into the
  199.  * resource as placeholders for the NULL characters, this has the undesireable
  200.  * effect of forcing us to search-and-replace byte-by-byte and doesn't make it
  201.  * as easy to internationalize our strings...
  202.  */
  203.     memset( szOpenFilter, 0, sizeof(szOpenFilter));
  204.     uCharsRead = LoadString( hInstance, IDS_OPEN_FILTER1,
  205.                 szOpenFilter, sizeof(szOpenFilter)) + 1;
  206.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER2,
  207.                     &szOpenFilter[uCharsRead],
  208.                     sizeof(szOpenFilter) - uCharsRead ) + 1;
  209.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER3,
  210.                     &szOpenFilter[uCharsRead],
  211.                     sizeof(szOpenFilter) - uCharsRead ) + 1;
  212.     LoadString( hInstance, IDS_OPEN_FILTER4,
  213.                     &szOpenFilter[uCharsRead],
  214.                     sizeof(szOpenFilter) - uCharsRead );
  215.  
  216.     /* Calculate the size of the client window */
  217.     cx = CONTROL_SPACE_CX + 2*BORDER_SPACE_CX + BUTTON_CX + PAN_TB_CX
  218.     + 2*GetSystemMetrics( SM_CXBORDER ) + PAN_TEXT_CX + TEXT_SPACE_CX;
  219.  
  220.     cy = 2*(BORDER_SPACE_CY + GetSystemMetrics( SM_CYBORDER ))
  221.     + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY + PROG_TB_CY
  222.     + GetSystemMetrics( SM_CYMENU ) + 3*CONTROL_SPACE_CY
  223.     + GetSystemMetrics( SM_CYCAPTION );
  224.  
  225.     /* Create an application window */
  226. #ifdef DEBUG
  227.     hWnd = CreateWindow( szAppClass,        /* class name */
  228.             szAppCaption,        /* caption for window */
  229.             WS_OVERLAPPEDWINDOW    /* style */
  230.              & ~WS_THICKFRAME | WS_BORDER,
  231.             CW_USEDEFAULT,        /* x position */
  232.             CW_USEDEFAULT,        /* y position */
  233.             cx,            /* width */
  234.             cy + 200,               /* height */
  235.             NULL,            /* parent window */
  236.             NULL,            /* menu */
  237.             hInstance,        /* instance */
  238.             NULL );            /* parms */
  239. #else
  240.     hWnd = CreateWindow( szAppClass,        /* class name */
  241.             szAppCaption,        /* caption for window */
  242.             WS_OVERLAPPEDWINDOW    /* style */
  243.              & ~WS_THICKFRAME | WS_BORDER,
  244.             CW_USEDEFAULT,        /* x position */
  245.             CW_USEDEFAULT,        /* y position */
  246.             cx,            /* width */
  247.             cy,            /* height */
  248.             NULL,            /* parent window */
  249.             NULL,            /* menu */
  250.             hInstance,        /* instance */
  251.             NULL );            /* parms */
  252. #endif
  253.  
  254.     if( !hWnd )
  255.         {
  256.     ErrorMessageBox( IDS_ERROR_MAINWNDCREATE, MB_ICONSTOP );
  257.         return( FALSE );
  258.     }
  259.  
  260.     hWndMain = hWnd;
  261.     GetClientRect( hWndMain, &crect );
  262.  
  263.     // Apparently WM_INITMENU isn't called for the main window's context version
  264.     // of the system menu, so we must gray these at startup as well.
  265.     hSysMenu = GetSystemMenu( hWndMain, FALSE );
  266.     EnableMenuItem( hSysMenu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED );
  267.     EnableMenuItem( hSysMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED );
  268.  
  269. #ifdef DEBUG
  270.     cy = 2*BORDER_SPACE_CY + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY + PROG_TB_CY
  271.             + 3*CONTROL_SPACE_CY;
  272.  
  273.     hWndList = CreateWindow( "listbox", NULL, WS_CHILD | WS_VISIBLE
  274.                     | LBS_NOINTEGRALHEIGHT | WS_VSCROLL,
  275.                 0, cy, crect.right-crect.left,
  276.                 crect.bottom - crect.top - cy,
  277.                 hWnd, NULL, hInstance, NULL );
  278. #endif
  279.  
  280.     /* Create some controls for things like volume, panning, etc. */
  281.     if( CreateChildren( crect ))
  282.         return( FALSE );
  283.  
  284.     ShowWindow( hWnd, nCmdShow );
  285.     UpdateWindow( hWnd );
  286.  
  287.     /* Create the main DirectSound object */
  288.  
  289.     bEnumDrivers = GetProfileInt( "DSSTREAM", "EnumDrivers", FALSE );
  290.     if( bEnumDrivers && !DoDSoundEnumerate( &guID ))
  291.     {
  292.     fUseGuid = TRUE;
  293.     }
  294.     dsRetVal = DirectSoundCreate( fUseGuid ? &guID : NULL, &lpDS, NULL );
  295.  
  296.     
  297.     if( dsRetVal != DS_OK )
  298.         {
  299.         ErrorMessageBox( IDS_ERROR_DSCREATE, MB_ICONSTOP );
  300.         return( FALSE );
  301.         }
  302.  
  303.     dsRetVal = lpDS->lpVtbl->SetCooperativeLevel( lpDS,
  304.                                                 hWndMain,
  305.                                                 DSSCL_NORMAL );
  306.     if( dsRetVal != DS_OK )
  307.         {
  308.         ErrorMessageBox( IDS_ERROR_DSCOOPERATIVE, MB_ICONSTOP );
  309.         return( FALSE );
  310.         }
  311.  
  312.  
  313.     return( TRUE );
  314.     } /* End of InitInstance() */
  315.  
  316.  
  317. /****************************************************************************/
  318. /* MainWindowProc()                                                         */
  319. /*                                                                          */
  320. /*    Messages for our main window are handled here                         */
  321. /*                                                                          */
  322. /****************************************************************************/
  323. LRESULT CALLBACK MainWindowProc( HWND hWnd, unsigned uMsg,
  324.                         WPARAM wParam, LPARAM lParam )
  325.     {
  326. #ifndef DEBUG
  327.     LPMINMAXINFO lpMinMax;
  328. #endif
  329.     DWORD   dwCDErr = 0, dwProg;
  330.     float   fPercent;
  331.     UINT    uPercent;
  332.     BOOL    bResult = FALSE;
  333.     int        nChkErr;
  334.     HRESULT dsrval;
  335.  
  336.     switch( uMsg )
  337.         {
  338.         case WM_DSSTREAM_PROGRESS:
  339.             dwProg = (DWORD)lParam;
  340.             dwProg  = dwProg % wiWave.mmckInRIFF.cksize;
  341.             fPercent = (float)((dwProg * 100)/ wiWave.mmckInRIFF.cksize);  
  342.             SendMessage( hWndProg, TBM_SETPOS,TRUE, (DWORD)(float)(fPercent*(float)PROG_MULTIPLIER));
  343.             uPercent = (UINT)fPercent;
  344.             wsprintf( szTemp, "%s: %u%%", szProgress, uPercent );
  345.             Static_SetText( hWndProgText, szTemp );
  346.             break;
  347.  
  348.          /*    This message will be posted by the helper DLL when the TimeFunc
  349.          * is done streaming the WAVE file. It serves as notification that the
  350.          * caller should terminate WAVE playback and end the MM timer event.
  351.          */
  352.         case WM_DSSTREAM_DONE:
  353.             /* Emulate a WM_COMMAND to ourselves */
  354.             DPF(0, "received a dsstream_done message");
  355.             PostMessage( hWnd, WM_COMMAND, MAKEWPARAM( IDM_STOP, 0 ), 0L );
  356.             break;
  357.  
  358. #ifdef DEBUG
  359.         case WM_DSSTREAM_DEBUG:
  360.             if( LOWORD(wParam) == DEBUGF_PLAYPOSITION )
  361.                 {
  362.                 wsprintf( szDebug, "pp = %li", lParam );
  363.                 ListBox_AddString( hWndList, szDebug );
  364.                 DPF( 4, szDebug );
  365.                 }
  366.             else if( LOWORD(wParam) == DEBUGF_WRITEPOSITION )
  367.                 {
  368.                 wsprintf( szDebug, "wp = %li", lParam );
  369.                 ListBox_AddString( hWndList, szDebug );
  370.                 DPF( 4, szDebug );
  371.                 }
  372.             else if( LOWORD(wParam) == DEBUGF_NEXTWRITE )
  373.                 {
  374.                 wsprintf( szDebug, "nw = %li", lParam );
  375.                 ListBox_AddString( hWndList, szDebug );
  376.                 DPF( 4, szDebug );
  377.                 }
  378.             else if( LOWORD(wParam) == DEBUGF_SKIP )
  379.                 {
  380.                 ListBox_AddString( hWndList, "Skipped segment read" );
  381.                 DPF( 5, szDebug );
  382.                 }
  383.             break;
  384. #endif
  385.  
  386.         case WM_COMMAND:
  387.             switch( LOWORD( wParam ))
  388.                 {
  389.                 case IDM_FILE_OPEN:
  390.                     {
  391.                     OPENFILENAME    ofn;
  392.                     /*
  393.                      * Clear out and fill in an OPENFILENAME structure in preparation
  394.                      * for creating a common dialog box to open a file.
  395.                      */
  396.                     memset( &ofn, 0, sizeof(OPENFILENAME));
  397.                     ofn.lStructSize    = sizeof(OPENFILENAME);
  398.                     ofn.hwndOwner    = hWnd;
  399.                     ofn.hInstance    = hInst;
  400.                     ofn.lpstrFilter    = szOpenFilter;
  401.                     ofn.nFilterIndex    = 1;
  402.                     szFileBuffer[0]    = '\0';
  403.                     ofn.lpstrFile    = szFileBuffer;
  404.                     ofn.nMaxFile    = sizeof(szFileBuffer);
  405.                     ofn.lpstrFileTitle    = szFileTitle;
  406.                     ofn.nMaxFileTitle    = sizeof(szFileTitle);
  407.                      ofn.lpstrInitialDir = szCDStartPath;
  408.                     ofn.lpstrDefExt    = "WAV";
  409.                     ofn.lpstrTitle    = szOpenDLGTitle;
  410.                     ofn.Flags        = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
  411.  
  412.                     bResult = GetOpenFileName( &ofn ); /* Do the dialog box */
  413.                     /*
  414.                      *    A return of TRUE indicates that the user did not select a filename.
  415.                      * The possible reasons are: Cancel was clicked, or an error occured.
  416.                      * If Cancel was clicked, the CommDlgExtendedError() function will not
  417.                      * return a valid code.  For anything else, an error code will come back.
  418.                      */
  419.                     if( bResult == FALSE )
  420.                         {
  421.                         dwCDErr = CommDlgExtendedError();
  422.                         if( dwCDErr )
  423.                             {
  424.                             /* Handle a common dialog box error */
  425.                             HandleCommDlgError( dwCDErr );
  426.                             }
  427.                         else    /* Clicked Cancel, so finish msg processing */
  428.                             return( 0 );
  429.                         }
  430.                     else
  431.                         {
  432.                         // Copy the directory name we opened from so that we start there
  433.                         // next time we open the dialog box...
  434.                         lstrcpy( szCDStartPath, szFileBuffer );
  435.                         szCDStartPath[ofn.nFileOffset] = '\0';
  436.  
  437.                         if( bFileOpen )
  438.                             {
  439.                         // Before we force a close, disable auto close
  440.                         // because the user has picked a new file and is
  441.                         // obviously not ready for us to go away yet.
  442.                             gfCloseOnDone = FALSE;
  443.                         /* Need to close the previous file before we open a new one.  The best
  444.                          * way to do this is by faking a menu command, so that we only have the
  445.                          * actual code in one place and it can easily be changed.
  446.                          */
  447.                             SendMessage( hWnd, WM_COMMAND,
  448.                                     MAKEWPARAM( IDM_FILE_CLOSE, 0 ), 0L );
  449.                             }
  450.                     
  451.                             if(( nChkErr = StreamBufferSetup()) != 0 )
  452.                             {
  453.                                 // Error opening the WAVE file so abort
  454.                                 break;
  455.                             }
  456.                         else
  457.                             {
  458.                             bFileOpen = TRUE;
  459.                             EnableMenuItem( GetMenu( hWnd ), IDM_PLAY,
  460.                                         MF_BYCOMMAND | MF_ENABLED );
  461.                             EnableWindow( hWndPlay, TRUE );
  462.                             EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  463.                                         MF_BYCOMMAND | MF_ENABLED );
  464.                             DrawMenuBar( hWnd );
  465.                             BuildTitleBarText();
  466.                             }
  467.                         }
  468.                     }
  469.                     break;
  470.  
  471.                 case IDM_FILE_CLOSE:
  472.                     SendMessage( hWnd, WM_COMMAND,
  473.                             MAKEWPARAM( IDM_STOP,
  474.                                             DSSTREAM_STOPF_NOREOPEN ), 0L );
  475.             BuildTitleBarText();
  476.                     break;
  477.  
  478.                 case IDM_OPTIONS_ENUMDRIVERS:
  479.                     bEnumDrivers = !bEnumDrivers;
  480.                     if( bEnumDrivers )
  481.                         {
  482.                         LoadString( hInst, IDS_ENUMWARNING, szTemp, sizeof(szTemp));
  483.                         MessageBox( hWnd, szTemp, szAppCaption, MB_OK );
  484.                         }
  485.                     break;
  486.  
  487.                 case IDM_HELP_ABOUT:
  488.                     DialogBox( hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndMain,
  489.                             (DLGPROC)DLG_About );
  490.                     break;
  491.  
  492.                 case IDC_LOOPCHECK:
  493.                     wiWave.bLoopFile = !wiWave.bLoopFile;
  494.                     Button_SetCheck( hWndLoopCheck, wiWave.bLoopFile );
  495.                     if( !bPlaying && bFileOpen )
  496.                         ResetWavePlayer();
  497.                     break;
  498.  
  499.                 case IDM_PLAY:
  500.                     DPF(0, "In idm_play");
  501.                     if( bPlaying )
  502.                     {
  503.                         gfCloseOnDone = FALSE;
  504.                         SendMessage( hWnd, WM_COMMAND, MAKEWPARAM( IDM_STOP, 0 ), 0L );
  505.                     }
  506.  
  507.                     if( bFileOpen && lpDSBStreamBuffer )
  508.                     {
  509.                         dsrval = SetupNotifications();
  510.                         if (dsrval != DS_OK)
  511.                         {
  512.                             DPF(0, "In Play command, setupnotifications failed");
  513.                             break;
  514.                         }
  515.  
  516.                         // Ensure that position is at 0, ready to go
  517.                         dsrval = lpDSBStreamBuffer->lpVtbl->SetCurrentPosition(lpDSBStreamBuffer, 0 );
  518.                         ASSERT(dsrval == DS_OK);
  519. #ifdef DEBUG
  520.                         {
  521.                             DWORD dwWrite, dwPlay;
  522.                         dsrval = lpDSBStreamBuffer->lpVtbl->GetCurrentPosition(lpDSBStreamBuffer, &dwPlay, &dwWrite);
  523.                         ASSERT(dsrval == DS_OK);
  524.                         if (dwPlay != 0)
  525.                             DPF(0, "Couldn't set pos to 0");
  526.                         }
  527. #endif 
  528.                         wiWave.bDonePlaying = FALSE;
  529.                         DPF(0, "calling play()");
  530.                         dsrval = lpDSBStreamBuffer->lpVtbl->Play( lpDSBStreamBuffer,0, 0, DSBPLAY_LOOPING );                                                      
  531.                     }
  532.                     else
  533.                     {
  534.                         bPlaying = FALSE;
  535.                         break;
  536.                     }
  537.  
  538.                     bPlaying = TRUE;
  539.                     EnableWindow(hWndPlay,FALSE);
  540.                     EnableMenuItem(GetMenu( hWnd ), IDM_PLAY, MF_BYCOMMAND | MF_GRAYED );
  541.                     EnableMenuItem(GetMenu( hWnd ), IDM_STOP, MF_BYCOMMAND | MF_ENABLED );
  542.                     EnableWindow( hWndStop, TRUE );
  543.                     DrawMenuBar( hWnd );
  544.                     break;
  545.  
  546.  
  547.                 case IDM_STOP:
  548.                     DPF(0, "received a idm_stop");
  549.                     if (gfCloseOnDone)
  550.                         wiWave.bDonePlaying = TRUE;
  551.                     if( bPlaying )
  552.                         {
  553.                             bPlaying = FALSE;
  554.                             dsrval = lpDSBStreamBuffer->lpVtbl->Stop( lpDSBStreamBuffer );
  555.                             EnableMenuItem(GetMenu( hWnd ), IDM_STOP,MF_BYCOMMAND | MF_GRAYED );                                            
  556.                             EnableWindow(hWndStop, FALSE );
  557.                             DrawMenuBar( hWnd );
  558.                         }
  559.                         // Short circuit to allow command-line forced shutdown
  560.                         if(!( HIWORD(wParam) & DSSTREAM_STOPF_NOREOPEN ) && !gfCloseOnDone )                            
  561.                         {
  562.                             ResetWavePlayer();
  563.                             EnableMenuItem(GetMenu( hWnd ), IDM_PLAY, MF_BYCOMMAND | MF_ENABLED );
  564.                             EnableWindow(hWndPlay, TRUE);
  565.                             DrawMenuBar( hWnd );
  566.                             break;
  567.                         }
  568.                         else
  569.                         {
  570.                         if( bFileOpen )
  571.                             {
  572.                                 DPF(0, "In Stop: Closing read file");
  573.                                 WaveCloseReadFile( &wiWave.hmmio, &wiWave.pwfx );                                                            
  574.                                 if (lpDirectSoundNotify)
  575.                                     dsrval = lpDirectSoundNotify->lpVtbl->Release(lpDirectSoundNotify);
  576.                                 lpDirectSoundNotify = NULL;
  577.  
  578.                                 if( lpDSBStreamBuffer )
  579.                                     dsrval = lpDSBStreamBuffer->lpVtbl->Release(lpDSBStreamBuffer );
  580.                                 lpDSBStreamBuffer = NULL;
  581.                     
  582.                                 bFileOpen = FALSE;
  583.                                 // The file is closed, so disable the close option
  584.                                 EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  585.                                                 MF_BYCOMMAND | MF_GRAYED );
  586.                                 EnableMenuItem( GetMenu( hWnd ), IDM_PLAY,
  587.                                                 MF_BYCOMMAND | MF_GRAYED );
  588.                                 EnableWindow( hWndPlay, FALSE );
  589.                                 DrawMenuBar( hWnd );
  590.                                 PostMessage( hWnd, WM_DSSTREAM_PROGRESS, 0L, 0L );
  591.                                 if( gfCloseOnDone &&
  592.                                     !(HIWORD(wParam) && DSSTREAM_STOPF_NOEXIT))
  593.                                 SendMessage( hWnd, WM_COMMAND,
  594.                                     MAKEWPARAM( IDM_FILE_EXIT, 0 ), 0L );
  595.                                 }
  596.                             }
  597.                         break;
  598.  
  599.                 case IDM_FILE_EXIT:
  600.                     DestroyWindow( hWnd );
  601.                     break;
  602.                 }
  603.             break;
  604. #ifndef DEBUG
  605.         case WM_GETMINMAXINFO:
  606.     /*
  607.      * We know exactly how big this window should be, and it's sort of a
  608.      * little pop-up control panel, so we can disable window sizing by
  609.      * forcing all the minimum and maximum sizes to be the calculated size.
  610.      */
  611.             lpMinMax = (LPMINMAXINFO)lParam;
  612.  
  613.             lpMinMax->ptMaxSize.x = CONTROL_SPACE_CX + 2*BORDER_SPACE_CX
  614.                                     + BUTTON_CX + PAN_TB_CX + PAN_TEXT_CX
  615.                                     + TEXT_SPACE_CX
  616.                                     + 2*GetSystemMetrics( SM_CXBORDER );
  617.             lpMinMax->ptMaxSize.y = 2*(BORDER_SPACE_CY
  618.                                     + GetSystemMetrics( SM_CYBORDER ))
  619.                                     + PAN_TB_CY + VOL_TB_CY + FREQ_TB_CY
  620.                                     + PROG_TB_CY + 3*CONTROL_SPACE_CY
  621.                                     + GetSystemMetrics( SM_CYMENU )
  622.                                     + GetSystemMetrics( SM_CYCAPTION );
  623.  
  624.             lpMinMax->ptMinTrackSize.x = lpMinMax->ptMaxTrackSize.x
  625.                                         = lpMinMax->ptMaxSize.x;
  626.  
  627.             lpMinMax->ptMinTrackSize.y = lpMinMax->ptMaxTrackSize.y
  628.                                         = lpMinMax->ptMaxSize.y;
  629.             break;
  630. #endif
  631.         case WM_HSCROLL:
  632.             if(((HWND)lParam == hWndPan) && lpDSBStreamBuffer )
  633.                 {
  634.                 HandlePanScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  635.                 }
  636.             else if(((HWND)lParam == hWndVol) && lpDSBStreamBuffer )
  637.                 {
  638.                 HandleVolScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  639.                 }
  640.             else if(((HWND)lParam == hWndFreq) && lpDSBStreamBuffer )
  641.                 {
  642.                 HandleFreqScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  643.                 }
  644.             break;
  645.  
  646.         case WM_INITMENU:
  647.         {
  648.         HMENU    hSysMenu = GetSystemMenu( hWnd, FALSE );
  649.  
  650.         EnableMenuItem( hSysMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED );
  651.         EnableMenuItem( hSysMenu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED );
  652.  
  653.         if((HMENU)wParam != GetMenu( hWnd ))
  654.                 break;
  655.             CheckMenuItem((HMENU)wParam, IDM_OPTIONS_ENUMDRIVERS,
  656.                                 bEnumDrivers ? MF_CHECKED : MF_UNCHECKED );
  657.         }
  658.             break;
  659.  
  660.         case WM_DESTROY:
  661.             /*
  662.              * Free all the DirectSound objects we created
  663.              */
  664.         SendMessage( hWnd, WM_COMMAND,
  665.                           MAKEWPARAM( IDM_STOP, DSSTREAM_STOPF_NOREOPEN
  666.                         | DSSTREAM_STOPF_NOEXIT ), 0 );
  667.  
  668.  
  669.             if( lpDS ) dsrval = lpDS->lpVtbl->Release( lpDS );
  670.  
  671.             WriteProfileString( "DSSTREAM", "EnumDrivers",
  672.                                         bEnumDrivers ? "1" : "0" );
  673.  
  674.             PostQuitMessage( 0 );
  675.             break;
  676.  
  677.         default:
  678.             return DefWindowProc( hWnd, uMsg, wParam, lParam );
  679.         }
  680.     return 0L;
  681.     } /* WindowProc */
  682.  
  683.  
  684. /*****************************************************************************/
  685. /* DLG_About()                                                               */
  686. /*                                                                           */
  687. /*   Dialog procedure for the Help...About... box which simply pops up a     */
  688. /* little copyright message and brief program description.                   */
  689. /*                                                                           */
  690. /*****************************************************************************/
  691. BOOL CALLBACK DLG_About( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  692.     {
  693.     switch( msg )
  694.         {
  695.         case WM_INITDIALOG:
  696.             break;
  697.  
  698.         case WM_COMMAND:
  699.             switch( LOWORD(wParam))
  700.                 {
  701.                 case IDOK:
  702.                     EndDialog( hDlg, FALSE );
  703.                     return( TRUE );
  704.  
  705.                 default:
  706.                     break;
  707.                 }
  708.             break;
  709.  
  710.         default:
  711.             return( FALSE );
  712.         }
  713.  
  714.     return( FALSE );
  715.     }
  716.  
  717.  
  718. /*****************************************************************************/
  719. /* HandleCommDlgError()                                                      */
  720. /*                                                                           */
  721. /*    The function translates extended common dialog error codes into a      */
  722. /* string resource ID, loads that string from our module, and displays it in */
  723. /* a message box. This implementation only covers the general CD error codes.*/
  724. /*                                                                           */
  725. /*****************************************************************************/
  726. int HandleCommDlgError( DWORD dwError )
  727.     {
  728.     char szTitle[128];
  729.     UINT uMsgID;
  730.  
  731.     if( dwError == CDERR_DIALOGFAILURE )
  732.         uMsgID = IDS_CDERR_DIALOGFAILURE;
  733.     else
  734.         uMsgID = (UINT)dwError + IDS_CDERR_GENERAL_BASE;
  735.  
  736.     LoadString( hInst, uMsgID, szTemp, sizeof(szTemp));
  737.     LoadString( hInst, IDS_CDERR_TITLESTRING, szTitle, sizeof(szTitle));
  738.     MessageBox( GetActiveWindow(), szTemp, szTitle,
  739.                     MB_OK | MB_ICONEXCLAMATION );
  740.         
  741.     return( 0 );
  742.     }
  743.  
  744.  
  745. /*****************************************************************************/
  746. /* StreamBufferSetup()                                                       */
  747. /*                                                                           */
  748. /* This function uses the filename stored in the global character array to*/
  749. /* open a WAVE file. Then it creates a secondary DirectSoundBuffer object    */
  750. /* which will later be used to stream that file from disk during playback.   */
  751. /*                                                                           */
  752. /*****************************************************************************/
  753. int StreamBufferSetup( void )
  754.     {
  755.     DSBUFFERDESC dsbd;
  756.     HRESULT      dsRetVal;
  757.  
  758.     int nChkErr;
  759.     int nRem;
  760.  
  761.     /* This portion of the WAVE I/O is patterned after what's in DSTRWAVE, which
  762.      * was in turn adopted from WAVE.C which is part of the DSSHOW sample.
  763.      */
  764.  
  765.     if(( nChkErr = WaveOpenFile( szFileBuffer, &wiWave.hmmio, &wiWave.pwfx, &wiWave.mmckInRIFF )) != 0 )
  766.         {
  767.         ErrorMessageBox( IDS_ERROR_WAVEFILEOPEN, MB_ICONEXCLAMATION );
  768.         return( ERR_WAVE_OPEN_FAILED );
  769.         }
  770.  
  771.     if( wiWave.pwfx->wFormatTag != WAVE_FORMAT_PCM )
  772.         {
  773.         ErrorMessageBox( IDS_ERROR_WAVENOTPCM, MB_ICONEXCLAMATION );
  774.         WaveCloseReadFile( &wiWave.hmmio, &wiWave.pwfx );
  775.         return( ERR_WAVE_INVALID_FORMAT );
  776.         }
  777.     // Seek to the data chunk. mmck.ckSize will be the size of all the data in the file.
  778.     if(( nChkErr = WaveStartDataRead( &wiWave.hmmio, &wiWave.mmck, &wiWave.mmckInRIFF )) != 0 )
  779.         {
  780.         ErrorMessageBox( IDS_ERROR_WAVESEEKFAILED, MB_ICONEXCLAMATION );
  781.         WaveCloseReadFile( &wiWave.hmmio, &wiWave.pwfx );
  782.         return( ERR_WAVE_CORRUPTED_FILE );
  783.         }
  784.     
  785.     // Calculate a buffer length 3 sec. long. This should be an integral number of the
  786.     // number of bytes in one notification period. 
  787.     wiWave.dwNotifySize = wiWave.pwfx->nSamplesPerSec * 3 * (DWORD)wiWave.pwfx->nBlockAlign;
  788.     wiWave.dwNotifySize = wiWave.dwNotifySize/NUM_PLAY_NOTIFICATIONS;
  789.     // the notify size should be an intergral multiple of the nBlockAlignvalue.
  790.     if ((nRem = wiWave.dwNotifySize%(DWORD)wiWave.pwfx->nBlockAlign) != 0)
  791.     {
  792.         wiWave.dwNotifySize += (wiWave.pwfx->nBlockAlign - nRem);
  793.     }
  794.     wiWave.dwBufferSize = wiWave.dwNotifySize * NUM_PLAY_NOTIFICATIONS;    
  795.     DPF(0, "BufferSize = %lu ", wiWave.dwBufferSize);
  796.  
  797. #ifdef DEBUG
  798.     wsprintf( szDebug, "BufferSize = %lu", wiWave.dwBufferSize );
  799.     ListBox_AddString( hWndList, szDebug );
  800. #endif
  801.  
  802.      //Create the secondary DirectSoundBuffer object to receive our sound data.
  803.     memset( &dsbd, 0, sizeof( DSBUFFERDESC ));
  804.     dsbd.dwSize = sizeof( DSBUFFERDESC );
  805.     // Use new GetCurrentPosition() accuracy (DirectX 2 feature)
  806.     dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY| DSBCAPS_CTRLDEFAULT | DSBCAPS_GETCURRENTPOSITION2 | gdwFocus;
  807.     dsbd.dwBufferBytes = wiWave.dwBufferSize;
  808.  
  809.     //Set Format properties according to the WAVE file we just opened
  810.     dsbd.lpwfxFormat = wiWave.pwfx;
  811.     
  812.     dsRetVal = lpDS->lpVtbl->CreateSoundBuffer( lpDS, &dsbd,&lpDSBStreamBuffer,NULL );                        
  813.     if( dsRetVal != DS_OK )
  814.         {
  815.         ErrorMessageBox( IDS_ERROR_DSBCREATE, MB_ICONEXCLAMATION );
  816.         return( ERR_CREATEDSB_FAILED );
  817.         }
  818.  
  819.     wiWave.lpDSBStreamBuffer = lpDSBStreamBuffer;
  820.     wiWave.bFoundEnd = FALSE;
  821.     wiWave.dwNextWriteOffset = 0;
  822.     wiWave.bLoopFile = Button_GetCheck( hWndLoopCheck );
  823.  
  824.     // now get the pointer to the notification interface.
  825.     dsRetVal = IDirectSoundNotify_QueryInterface(lpDSBStreamBuffer, &IID_IDirectSoundNotify, &((LPVOID)lpDirectSoundNotify));
  826.     if (dsRetVal != DS_OK)
  827.     {    
  828.         ErrorMessageBox(IDS_ERROR_QINOTIFY_FAILED, MB_ICONEXCLAMATION);
  829.         return (dsRetVal);
  830.     }
  831.  
  832.     // Fill data in the buffer.
  833.     FillDataBuffer();    
  834.  
  835.     // we're set to play now.
  836.     wiWave.bDonePlaying = FALSE;
  837.  
  838. #ifdef DEBUG
  839.     wsprintf( szDebug, "wiWave.dwBufferSize = %lu", wiWave.dwBufferSize );
  840.     DPF( 3, "StreamBufferSetup Debug" );
  841.     DPF( 3, szDebug );
  842. #endif
  843.  
  844.     SendMessage( hWndVol, TBM_SETPOS, TRUE, VOL_MAX );
  845.     SendMessage( hWndPan, TBM_SETPOS, TRUE, PAN_CENTER );
  846.     SendMessage( hWndFreq, TBM_SETPOS, TRUE,(LPARAM)wiWave.pwfx->nSamplesPerSec / FREQ_MULTIPLIER );
  847.     PostMessage( hWndMain, WM_DSSTREAM_PROGRESS, 0L, 0L );
  848.     UpdateFromControls();
  849.     return( 0 );
  850.     }
  851.  
  852.  
  853. /*****************************************************************************/
  854. /* ResetWavePlayer()                                                         */
  855. /*                                                                           */
  856. /*  Performs a subset of the above operations (in StreamBufferSetup). Things */
  857. /* not done include creating a DSB and opening the file (it's already open). */
  858. /*                                                                           */
  859. /*****************************************************************************/
  860. void ResetWavePlayer( void )
  861.     {
  862.     int nChkErr = DS_OK;
  863.     DPF(1, "Resetting wave player");
  864.     ASSERT(bPlaying == FALSE);
  865.  
  866.     /* Seek to the data chunk */
  867.     if(( nChkErr = WaveStartDataRead( &wiWave.hmmio, &wiWave.mmck, &wiWave.mmckInRIFF )) != 0 )
  868.         {
  869.         // This shouldn't happen, since we've done this before.
  870.         DPF(0, "Error seeking to file in ResetWavePlayer");
  871.         return;    
  872.         }
  873.  
  874.     wiWave.bFoundEnd = FALSE;
  875.     wiWave.dwNextWriteOffset = 0;
  876.     
  877.     DPF(0, "Reset: Filldatabuffer");
  878.     FillDataBuffer();
  879.  
  880.     wiWave.bDonePlaying = FALSE;
  881.     PostMessage( hWndMain, WM_DSSTREAM_PROGRESS, 0L, 0L );
  882.     }
  883.  
  884.  
  885. /*****************************************************************************/
  886. /* FillDataBuffer()                                                          */
  887. /*                                                                           */
  888. /*   This function fills the sound buffer with a block of data, starting at  */
  889. /* the current read position.  The entire buffer is filled.                  */
  890. /*                                                                           */
  891. /*****************************************************************************/
  892.  void FillDataBuffer( void )
  893.  {
  894.     LPBYTE  lpWrite1, lpWrite2;
  895.     DWORD    dwLen1, dwLen2;
  896.     UINT    uActualBytesWritten;
  897.     int        nChkErr;
  898.     HRESULT    dsRetVal;
  899.     DWORD dwBytes = wiWave.dwBufferSize; 
  900.  
  901.     // This is the initial read. So we fill the entire buffer.
  902.     // This will not wrap around so the 2nd pointers will be NULL.
  903.     dsRetVal = lpDSBStreamBuffer->lpVtbl->Lock( lpDSBStreamBuffer, 0, dwBytes,
  904.                                                 &((LPVOID)lpWrite1), &dwLen1,
  905.                                                 &(LPVOID)lpWrite2, &dwLen2, 0 );
  906.     ASSERT(lpWrite2 == NULL);    
  907.     ASSERT(dwLen2 == 0);
  908.  
  909.     if (dwLen1 != dwBytes)
  910.     {
  911.         DPF(0, "FillDataBuffer: dwBytes != dwLen1");
  912.     }
  913.  
  914.  
  915.     if( dsRetVal != DS_OK )
  916.     {
  917.         ASSERT(FALSE);
  918.         return; 
  919.     }
  920.  
  921.     ASSERT(dwLen1);
  922.     ASSERT( NULL != lpWrite1 );
  923.     ASSERT(wiWave.dwNextWriteOffset < wiWave.dwBufferSize);
  924.  
  925.     nChkErr = WaveReadFile( wiWave.hmmio, (UINT)dwLen1, lpWrite1,
  926.                             &wiWave.mmck, &uActualBytesWritten );
  927.     // if the number of bytes written is less than the 
  928.     // amount we requested, we have a short file.
  929.     if (uActualBytesWritten < dwLen1)
  930.     {
  931.         DPF(0, "FillDataBuffer: Actual written is less than dwlen1");
  932.         if (!wiWave.bLoopFile)
  933.         {
  934.             // we set the bFoundEnd flag if the length is less than
  935.             // one notify period long which is when the first notification comes in.
  936.             // The next notification will then call send a message to process a stop. 
  937.             if (uActualBytesWritten < wiWave.dwNotifySize)
  938.                 wiWave.bFoundEnd = TRUE;
  939.             // Fill in silence for the rest of the buffer.
  940.                 DPF(0, "Filling in silence");
  941.                 FillMemory(lpWrite1+uActualBytesWritten, dwLen1-uActualBytesWritten, 
  942.                         (BYTE)(wiWave.pwfx->wBitsPerSample == 8 ? 128 : 0));
  943.         }
  944.         else
  945.         {
  946.             // we are looping.
  947.             UINT uWritten = uActualBytesWritten;    // from previous call above.
  948.             while (uWritten < dwLen1)
  949.             {    // this will keep reading in until the buffer is full. For very short files.
  950.                 nChkErr = WaveStartDataRead( &wiWave.hmmio, &wiWave.mmck, &wiWave.mmckInRIFF );
  951.                 ASSERT(nChkErr == 0);    // we've already this before so shouldn't fail.
  952.                 nChkErr = WaveReadFile(wiWave.hmmio, (UINT)dwLen1-uWritten, 
  953.                                 lpWrite1 + uWritten, &wiWave.mmck, &uActualBytesWritten);
  954.                 uWritten += uActualBytesWritten;
  955.             } // while
  956.             ASSERT(wiWave.bFoundEnd == FALSE);
  957.         } // else
  958.     }
  959.  
  960.         // now unlock the buffer.
  961.     dsRetVal = lpDSBStreamBuffer->lpVtbl->Unlock( lpDSBStreamBuffer, (LPVOID)lpWrite1, dwLen1,        NULL, 0 );
  962.                                                                              
  963.     wiWave.dwNextWriteOffset += dwLen1;    
  964.     // this is a circular buffer. Do mod buffersize.
  965.     if (wiWave.dwNextWriteOffset >= wiWave.dwBufferSize)
  966.         wiWave.dwNextWriteOffset -= wiWave.dwBufferSize; 
  967.  
  968.     DPF(3, "Setting dwProgress = 0");
  969.     wiWave.dwProgress = 0;
  970.     wiWave.dwLastPos = 0;
  971. }
  972.  
  973.  
  974. /*****************************************************************************/
  975. /* CreateChildren()                                                          */
  976. /*                                                                           */
  977. /*   This function creates a bunch of child controls for the main window.    */
  978. /* Most of them are used for controling various things about a playing sound */
  979. /* file, like volume and panning. Returns FALSE if no errors, TRUE otherwise.*/
  980. /*                                                                           */
  981. /*****************************************************************************/
  982. int CreateChildren( RECT crect )
  983.     {
  984.     SIZE  Size;
  985.     HDC   hDC;
  986.     int      x, y;
  987.     UINT  uType;
  988.     char  szTemplate[128], szType[32];
  989.     LPSTR lpszControl;
  990.  
  991.     LoadString( hInst, IDS_ERROR_CHILDTEMPLATE, szTemplate, sizeof(szTemplate));
  992.  
  993.     /* Don't handle failure for this one, because the app will still run fine */
  994.     hWndBar = CreateWindow( "static", NULL,
  995.                             WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
  996.                             0, 0, crect.right, 2, hWndMain,
  997.                             (HMENU)0, hInst, NULL );
  998.  
  999.     hDC = GetDC( hWndMain );
  1000.     if( !GetTextExtentPoint32( hDC, szPan, strlen(szPan), &Size ))
  1001.         {
  1002.         ErrorMessageBox( IDS_ERROR_GETTEXTEXTENT, MB_ICONEXCLAMATION );
  1003.         ReleaseDC( hWndMain, hDC );
  1004.         return( TRUE );
  1005.         }
  1006.     ReleaseDC( hWndMain, hDC );
  1007.  
  1008.     y = BORDER_SPACE_CY;
  1009.  
  1010.     /* STATIC control -- text label for the pan trackbar */
  1011.     if(( hWndPanText = CreateWindow( "static", szPan,
  1012.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1013.                                     BORDER_SPACE_CX + PAN_TB_CX + TEXT_SPACE_CX,
  1014.                                     y + (PAN_TB_CY - Size.cy)/2,
  1015.                                     PAN_TEXT_CX, Size.cy,
  1016.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1017.         {
  1018.         lpszControl = szPan;
  1019.     uType = IDS_ERROR_STATICTEXT;
  1020.         goto DISPLAY_CREATE_FAILURE;
  1021.         }
  1022.  
  1023.     /* PAN (left to right balance) trackbar control */
  1024.     if(( hWndPan = CreateWindow( TRACKBAR_CLASS, NULL,
  1025.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1026.                                 BORDER_SPACE_CX,
  1027.                                 y, PAN_TB_CX, PAN_TB_CY,
  1028.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1029.         {
  1030.         lpszControl = szPan;
  1031.     uType = IDS_ERROR_TRACKBAR;
  1032.         goto DISPLAY_CREATE_FAILURE;
  1033.     }
  1034.  
  1035.     SendMessage( hWndPan, TBM_SETRANGE, FALSE, MAKELONG( PAN_MIN, PAN_MAX )); 
  1036.     SendMessage( hWndPan, TBM_SETPOS, TRUE, PAN_CENTER );
  1037.     SendMessage( hWndPan, TBM_SETPAGESIZE, 0L, PAN_PAGESIZE );
  1038.  
  1039.     y += PAN_TB_CY + CONTROL_SPACE_CY;
  1040.  
  1041.     /* STATIC control -- text label for the volume trackbar */
  1042.     if(( hWndVolText = CreateWindow( "static", szVolume,
  1043.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1044.                                     BORDER_SPACE_CX + VOL_TB_CX + TEXT_SPACE_CX,
  1045.                                     y + (VOL_TB_CY - Size.cy)/2,
  1046.                                     VOL_TEXT_CX, Size.cy,
  1047.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1048.         {
  1049.         lpszControl = szVolume;
  1050.     uType = IDS_ERROR_STATICTEXT;
  1051.         goto DISPLAY_CREATE_FAILURE;
  1052.     }
  1053.  
  1054.     /* Create the VOLUME trackbar */
  1055.     if(( hWndVol = CreateWindow( TRACKBAR_CLASS, NULL,
  1056.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1057.                                 BORDER_SPACE_CX,
  1058.                                 y, VOL_TB_CX, VOL_TB_CY,
  1059.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1060.         {
  1061.         lpszControl = szVolume;
  1062.     uType = IDS_ERROR_TRACKBAR;
  1063.         goto DISPLAY_CREATE_FAILURE;
  1064.     }
  1065.  
  1066.     SendMessage( hWndVol, TBM_SETRANGE, FALSE,
  1067.                                         MAKELONG( VOL_MIN, VOL_MAX ));
  1068.     SendMessage( hWndVol, TBM_SETPOS, TRUE, VOL_MAX );
  1069.     SendMessage( hWndVol, TBM_SETPAGESIZE, 0L, VOL_PAGESIZE );
  1070.  
  1071.     y += VOL_TB_CY + CONTROL_SPACE_CY;
  1072.  
  1073.     /* STATIC control -- text label for the frequency trackbar */
  1074.     if(( hWndFreqText = CreateWindow( "static", szFrequency,
  1075.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1076.                                     BORDER_SPACE_CX + FREQ_TB_CX + TEXT_SPACE_CX,
  1077.                                     y + (FREQ_TB_CY - Size.cy)/2,
  1078.                                     FREQ_TEXT_CX, Size.cy,
  1079.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1080.         {
  1081.         lpszControl = szFrequency;
  1082.     uType = IDS_ERROR_STATICTEXT;
  1083.         goto DISPLAY_CREATE_FAILURE;
  1084.     }
  1085.  
  1086.     /* Create the FREQUENCY trackbar */
  1087.     if(( hWndFreq = CreateWindow( TRACKBAR_CLASS, NULL,
  1088.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTTOM,
  1089.                                 BORDER_SPACE_CX,
  1090.                                 y, FREQ_TB_CX, FREQ_TB_CY,
  1091.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1092.         {
  1093.         lpszControl = szFrequency;
  1094.     uType = IDS_ERROR_TRACKBAR;
  1095.         goto DISPLAY_CREATE_FAILURE;
  1096.     }
  1097.  
  1098.     SendMessage( hWndFreq, TBM_SETRANGE, FALSE, MAKELONG( FREQ_MIN, FREQ_MAX ));
  1099.     SendMessage( hWndFreq, TBM_SETPOS, TRUE, FREQ_MAX );
  1100.     SendMessage( hWndFreq, TBM_SETPAGESIZE, 0L, FREQ_PAGESIZE );
  1101.  
  1102.     y += FREQ_TB_CY + CONTROL_SPACE_CY;
  1103.  
  1104.     /* STATIC control -- text label for the progress trackbar */
  1105.     if(( hWndProgText = CreateWindow( "static", szProgress,
  1106.                                     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1107.                                     BORDER_SPACE_CX + PROG_TB_CX + TEXT_SPACE_CX,
  1108.                                     y + (PROG_TB_CY - Size.cy)/2,
  1109.                                     PROG_TEXT_CX, Size.cy,
  1110.                                     hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1111.         {
  1112.         lpszControl = szProgress;
  1113.     uType = IDS_ERROR_STATICTEXT;
  1114.         goto DISPLAY_CREATE_FAILURE;
  1115.     }
  1116.  
  1117.     /* Create the PROGRESSS trackbar */
  1118.     if(( hWndProg = CreateWindow( TRACKBAR_CLASS, NULL,
  1119.                                 WS_CHILD | WS_VISIBLE | TBS_HORZ
  1120.                 | TBS_BOTTOM | WS_DISABLED,
  1121.                                 BORDER_SPACE_CX,
  1122.                                 y, PROG_TB_CX, PROG_TB_CY,
  1123.                                 hWndMain, (HMENU)0, hInst, NULL)) == NULL )
  1124.         {
  1125.         lpszControl = szProgress;
  1126.     uType = IDS_ERROR_TRACKBAR;
  1127.         goto DISPLAY_CREATE_FAILURE;
  1128.     }
  1129.  
  1130.     SendMessage( hWndProg, TBM_SETRANGE,
  1131.                 FALSE, MAKELPARAM( PROG_MIN, PROG_MAX ));
  1132.     SendMessage( hWndProg, TBM_SETPOS, TRUE, 0L );
  1133.  
  1134.     x = BORDER_SPACE_CX + PAN_TEXT_CX + TEXT_SPACE_CX
  1135.             + PAN_TB_CX + CONTROL_SPACE_CX;
  1136.     y += PROG_TB_CY;
  1137.     y -= 2*(BUTTON_CY + BUTTON_SPACE_CY) + CHECK_CY;
  1138.  
  1139.     /* Create the LOOPED CHECKBOX */
  1140.     LoadString( hInst, IDS_CHECK_LOOPED, szTemp, sizeof(szTemp));
  1141.     if(( hWndLoopCheck = CreateWindow( "button", szTemp,
  1142.                                 WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
  1143.                                 x, y, CHECK_CX, CHECK_CY, hWndMain,
  1144.                                 (HMENU)IDC_LOOPCHECK, hInst, NULL )) == NULL )
  1145.         {
  1146.         lpszControl = szTemp;
  1147.     uType = IDS_ERROR_CHECK;
  1148.         goto DISPLAY_CREATE_FAILURE;
  1149.     }
  1150.     y += CHECK_CY + BUTTON_SPACE_CY;
  1151.  
  1152.     /* Create the PLAY BUTTON */
  1153.     LoadString( hInst, IDS_BUTTON_PLAY, szTemp, sizeof(szTemp));
  1154.     if(( hWndPlay = CreateWindow( "button", szTemp,
  1155.                                     WS_CHILD | WS_VISIBLE | WS_DISABLED,
  1156.                                     x, y, BUTTON_CX, BUTTON_CY, hWndMain,
  1157.                                     (HMENU)IDM_PLAY, hInst, NULL )) == NULL )
  1158.         {
  1159.         lpszControl = szTemp;
  1160.     uType = IDS_ERROR_BUTTON;
  1161.         goto DISPLAY_CREATE_FAILURE;
  1162.     }
  1163.     y += BUTTON_CY + BUTTON_SPACE_CY;
  1164.  
  1165.     /* Create the STOP BUTTON */
  1166.     LoadString( hInst, IDS_BUTTON_STOP, szTemp, sizeof(szTemp));
  1167.     if(( hWndStop = CreateWindow( "button", szTemp,
  1168.                                     WS_CHILD | WS_VISIBLE | WS_DISABLED,
  1169.                                     x, y, BUTTON_CX, BUTTON_CY, hWndMain,
  1170.                                     (HMENU)IDM_STOP, hInst, NULL )) == NULL )
  1171.         {
  1172.         lpszControl = szTemp;
  1173.     uType = IDS_ERROR_BUTTON;
  1174.         goto DISPLAY_CREATE_FAILURE;
  1175.     }
  1176.  
  1177.     UpdateFromControls();
  1178.     goto RETURN_NORMAL;
  1179.  
  1180. DISPLAY_CREATE_FAILURE:
  1181.     LoadString( hInst, uType, szType, sizeof(szType));
  1182.     wsprintf( szTemp, szTemplate, lpszControl, szType );
  1183.     MessageBox( GetActiveWindow(), szTemp,
  1184.                         szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  1185.     return( TRUE );
  1186.  
  1187. RETURN_NORMAL:
  1188.     return( FALSE );
  1189.     }
  1190.  
  1191.  
  1192. /********************************************************************************/
  1193. /* UpdateFromControls()                                                         */
  1194. /*                                                                              */
  1195. /*    This function gets all the required values from the DirectSoundBuffer and */
  1196. /* updates the screen interface controls.                                       */
  1197. /*                                                                              */
  1198. /********************************************************************************/
  1199. void UpdateFromControls( void )
  1200.     {
  1201.     long    lPan, lVol, lFreq;
  1202.     HRESULT hr;
  1203.  
  1204.     lPan = (LONG)SendMessage( hWndPan, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1205.     lVol = (LONG)SendMessage( hWndVol, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1206.     lFreq = (LONG)SendMessage( hWndFreq, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1207.  
  1208.     /* Set the volume and then the pan */
  1209.     if( lpDSBStreamBuffer )
  1210.         {
  1211.         /* Set the volume */
  1212.         wsprintf( szTemp, "%s: %lidB", szVolume,
  1213.                                             ( lVol + VOL_SHIFT ) / VOL_DIV );
  1214.         Static_SetText( hWndVolText, szTemp );
  1215.  
  1216.         hr = lpDSBStreamBuffer->lpVtbl->SetVolume( lpDSBStreamBuffer,
  1217.                                             (((lVol+VOL_SHIFT) * VOL_MULT)) );
  1218.         if( hr != 0 )
  1219.             DPF( 0, "Unable to SetVolume in UpdateFromControls()" );
  1220.         else
  1221.             {
  1222.             wsprintf( szDebug, "Set volume to %lidB",
  1223.                                             ( lVol + VOL_SHIFT ) / VOL_DIV );
  1224.             DPF( 3, szDebug );
  1225.             }
  1226.  
  1227.         /* Set the Pan */
  1228.         wsprintf( szTemp, "%s: %lidB", szPan, ( lPan + PAN_SHIFT ) / PAN_DIV );
  1229.         Static_SetText( hWndPanText, szTemp );
  1230.  
  1231.         hr = lpDSBStreamBuffer->lpVtbl->SetPan( lpDSBStreamBuffer,
  1232.                                             (((lPan+PAN_SHIFT) * PAN_MULT)) );
  1233.         if( hr != 0 )
  1234.             DPF( 0, "Unable to SetPan in UpdateFromControls()" );
  1235.         else
  1236.             {
  1237.             wsprintf( szDebug, "Set pan to %lidB",
  1238.                                             ( lPan + PAN_SHIFT ) / PAN_DIV );
  1239.             DPF( 3, szDebug );
  1240.             }
  1241.  
  1242.         /* Set the frequency */
  1243.         wsprintf( szTemp, "%s: %liHz", szFrequency, lFreq * FREQ_MULTIPLIER );
  1244.         Static_SetText( hWndFreqText, szTemp );
  1245.  
  1246.         hr = lpDSBStreamBuffer->lpVtbl->SetFrequency( lpDSBStreamBuffer,
  1247.                                                         lFreq * FREQ_MULTIPLIER);
  1248.         if( hr != 0 )
  1249.             DPF( 0, "Unable to SetFrequency in UpdateFromControls()" );
  1250.         else
  1251.             {
  1252.             wsprintf( szDebug, "Set frequency to %liHz", lFreq*FREQ_MULTIPLIER );
  1253.             DPF( 3, szDebug );
  1254.             }
  1255.         }
  1256.         return;
  1257.     }
  1258.  
  1259.  
  1260. /********************************************************************************/
  1261. /* HandlePanScroll()                                                            */
  1262. /*                                                                              */
  1263. /*   Handles the pan trackbar scroll when a WM_HSCROLL is received.             */
  1264. /*                                                                              */
  1265. /********************************************************************************/
  1266. void HandlePanScroll( int nCode, int nPos )
  1267.     {
  1268.     long  lPan, lDelta;
  1269.  
  1270.     lPan = (LONG)SendMessage( hWndPan, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1271.  
  1272.     switch( nCode )
  1273.         {
  1274.         case TB_LINEUP:
  1275.             if( lPan >= PAN_MIN - 1 )
  1276.                 lDelta = -1;
  1277.             break;
  1278.         case TB_LINEDOWN:
  1279.             if( lPan <= PAN_MAX + 1 )
  1280.                 lDelta = 1;
  1281.             break;
  1282.         case TB_PAGEUP:
  1283.             if( lPan >= PAN_MIN - PAN_PAGESIZE )
  1284.                 lDelta = -16;
  1285.             break;
  1286.         case TB_PAGEDOWN:
  1287.             if( lPan <= PAN_MAX + PAN_PAGESIZE )
  1288.                 lDelta = 16;
  1289.             break;
  1290.         case TB_ENDTRACK:
  1291.             return;
  1292.         default:
  1293.             lDelta = 0;
  1294.         }
  1295.  
  1296.     if( lDelta )
  1297.         SendMessage( hWndPan, TBM_SETPOS, TRUE, lPan + lDelta );
  1298.     else
  1299.         SendMessage( hWndPan, TBM_SETPOS, TRUE, (long)nPos );
  1300.     UpdateFromControls();
  1301.     }
  1302.  
  1303.  
  1304. /********************************************************************************/
  1305. /* HandleVolScroll()                                                            */
  1306. /*                                                                              */
  1307. /*   Handles the volume trackbar scrolling when a WM_HSCROLL is received.       */
  1308. /*                                                                              */
  1309. /********************************************************************************/
  1310. void HandleVolScroll( int nCode, int nPos )
  1311.     {
  1312.     long  lVol, lDelta;
  1313.  
  1314.     lVol = (LONG)SendMessage( hWndVol, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1315.  
  1316.     switch( nCode )
  1317.         {
  1318.         case TB_LINEDOWN:
  1319.             if( lVol <= VOL_MAX - 1 )
  1320.                 lDelta = 1;
  1321.             break;
  1322.         case TB_LINEUP:
  1323.             if( lVol >= VOL_MIN + 1 )
  1324.                 lDelta = -1;
  1325.             break;
  1326.         case TB_PAGEDOWN:
  1327.             if( lVol <= VOL_MAX - VOL_PAGESIZE )
  1328.                 lDelta = 10;
  1329.             break;
  1330.         case TB_PAGEUP:
  1331.             if( lVol >= VOL_MIN + VOL_PAGESIZE )
  1332.                 lDelta = -10;
  1333.             break;
  1334.         case TB_ENDTRACK:
  1335.             return;
  1336.         default:
  1337.             lDelta = 0;
  1338.         }
  1339.  
  1340.     if( lDelta )
  1341.         SendMessage( hWndVol, TBM_SETPOS, TRUE, (lVol + lDelta));
  1342.     else
  1343.         SendMessage( hWndVol, TBM_SETPOS, TRUE, (long)nPos );
  1344.     UpdateFromControls();
  1345.     }
  1346.  
  1347.  
  1348. /********************************************************************************/
  1349. /* HandleFreqScroll()                                                           */
  1350. /*                                                                              */
  1351. /*   Handles the volume trackbar scrolling when a WM_HSCROLL is received.       */
  1352. /*                                                                              */
  1353. /********************************************************************************/
  1354. void HandleFreqScroll( int nCode, int nPos )
  1355.     {
  1356.     long  lFreq, lDelta;
  1357.  
  1358.     lFreq = (LONG)SendMessage( hWndFreq, TBM_GETPOS, (WPARAM)0, (LPARAM)0 );
  1359.  
  1360.     switch( nCode )
  1361.         {
  1362.         case TB_LINEDOWN:
  1363.             if( lFreq <= FREQ_MAX-1 )
  1364.                 lDelta = 1;
  1365.             break;
  1366.         case TB_LINEUP:
  1367.             if( lFreq >= FREQ_MIN+1 )
  1368.                 lDelta = -1;
  1369.             break;
  1370.         case TB_PAGEDOWN:
  1371.             if( lFreq <= FREQ_MAX - FREQ_PAGESIZE )
  1372.                 lDelta = 10;
  1373.             break;
  1374.         case TB_PAGEUP:
  1375.             if( lFreq >= FREQ_MIN + FREQ_PAGESIZE )
  1376.                 lDelta = -10;
  1377.             break;
  1378.         case TB_ENDTRACK:
  1379.             return;
  1380.         default:
  1381.             lDelta = 0;
  1382.         }
  1383.  
  1384.     if( lDelta )
  1385.         SendMessage( hWndFreq, TBM_SETPOS, TRUE, (lFreq + lDelta));
  1386.     else
  1387.         SendMessage( hWndFreq, TBM_SETPOS, TRUE, (long)nPos );
  1388.     UpdateFromControls();
  1389.     }
  1390.  
  1391.  
  1392. /****************************************************************************/
  1393. /* ErrorMessageBox()                                                        */
  1394. /*                                                                          */
  1395. /*   A little routine to load error messages from the string resource table */
  1396. /* and pop them up in a MessageBox() for the world to see. The dwMBFlags    */
  1397. /* parameter allows the caller to specify the type of icon to use.          */
  1398. /*                                                                          */
  1399. /****************************************************************************/
  1400. void ErrorMessageBox( UINT uID, DWORD dwMBFlags )
  1401.     {
  1402.     LoadString( hInst, uID, szTemp, sizeof(szTemp));
  1403.     MessageBox( GetActiveWindow(), szTemp, szAppTitle, MB_OK | dwMBFlags );
  1404.     }
  1405.  
  1406.  
  1407. /****************************************************************************/
  1408. /* BuildTitleBar()                                                          */
  1409. /*                                                                          */
  1410. /*   Small routine to build and set the title bar text depending on whether */
  1411. /* or not a file is open.                                                   */
  1412. /****************************************************************************/
  1413. void BuildTitleBarText( void )
  1414.     {
  1415.     char szTitle[ sizeof(szAppCaption) + MAX_PATH + sizeof(" - ")];
  1416.  
  1417.     lstrcpy( szTitle, szAppCaption );
  1418.     if( bFileOpen )
  1419.     {
  1420.     lstrcat( szTitle, " - " );
  1421.     lstrcat( szTitle, szFileTitle );
  1422.     }
  1423.     SetWindowText( hWndMain, szTitle );
  1424.     }
  1425.  
  1426.  
  1427. /****************************************************************************/
  1428. /* GetMediaStartPath()                                                      */
  1429. /*                                                                          */
  1430. /*   This helper function attempts to get the media directory for Direct3D, */
  1431. /* which is where all the installed DX wave files go. If it can't find that */
  1432. /* it settles for the media sub-directory of the Windows directory.         */
  1433. /****************************************************************************/
  1434. void GetMediaStartPath( void )
  1435.     {
  1436.     HKEY    hReg;
  1437.     DWORD   cbStartPathLen;
  1438.  
  1439.     if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1440.             gszRegKeyDirect3D,
  1441.             0, KEY_READ, &hReg ))
  1442.     {
  1443.         goto REG_OPEN_FAILED;
  1444.     }
  1445.     else
  1446.     {
  1447.     // Query the Registry for the path to the media directory
  1448.     cbStartPathLen = sizeof( szCDStartPath );
  1449.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, gszRegValueD3DPath,
  1450.                 NULL, NULL,
  1451.                 szCDStartPath, &cbStartPathLen ))
  1452.         {
  1453.         goto REG_OPEN_FAILED;
  1454.         }
  1455.     RegCloseKey( hReg );
  1456.     hReg = NULL;
  1457.     }
  1458.  
  1459.     return;
  1460.  
  1461. REG_OPEN_FAILED:
  1462.     // Start off by getting the Windows directory -- we're trying to build a
  1463.     // file path like "C:\WINDOWS\MEDIA", but the WINDOWS directory could be
  1464.     // named anything, so we must ask.
  1465.     GetWindowsDirectory( szCDStartPath, sizeof(szCDStartPath));
  1466.     // If there's no trailing backslash, append one
  1467.     if( lstrcmp( &szCDStartPath[lstrlen(szCDStartPath)], TEXT("\\") ))
  1468.     lstrcat( szCDStartPath, TEXT("\\"));
  1469.     // Now add on the MEDIA part of the path
  1470.     lstrcat( szCDStartPath, TEXT("MEDIA"));
  1471.     }
  1472.  
  1473.  
  1474. void LoadFromCommandLine( LPSTR lpszCmd )
  1475.     {
  1476.     LPSTR lpsz = lpszCmd;
  1477.     LPSTR lpToken;
  1478.     BOOL  fStartPlaying = FALSE, fStartLooping = FALSE;
  1479.     char  szToken[MAX_TOKEN_LEN+1];
  1480.     int   i;
  1481.  
  1482.     if( !lpsz )
  1483.     return;
  1484.  
  1485.     // Clear leading spaces
  1486.     while( *lpsz == ' ' )
  1487.     lpsz++;
  1488.     
  1489.     // If we need to accept more command-line parameters later, we can
  1490.     // extend the code below into a loop that searchs for each one.
  1491.     while( *lpsz == '/' || *lpsz == '-' )
  1492.     {
  1493.     // Don't advance lpsz until we're sure we really should be reading
  1494.     // this string (i.e. we recognize that it's the play command
  1495.     lpToken = ++lpsz;
  1496.     for( i = 0; i < MAX_TOKEN_LEN; i++ )
  1497.         {
  1498.         if( !*lpToken || *lpToken == ' ' )
  1499.         break;
  1500.         szToken[i] = *lpToken++;
  1501.         }
  1502.     szToken[i] = 0;
  1503.  
  1504.     if( !lstrcmpi( szToken, gszPlayToken ))
  1505.         {
  1506.         // Automagically start playing the file
  1507.         fStartPlaying = TRUE;
  1508.         lpsz = lpToken;
  1509.         }
  1510.     else if( !lstrcmpi( szToken, gszLoopToken ))
  1511.         {
  1512.         // Set the player in looping mode at startup
  1513.         fStartLooping = TRUE;
  1514.         lpsz = lpToken;
  1515.         }
  1516.     else if( !lstrcmpi( szToken, gszStickyToken ))
  1517.         {
  1518.         // Use Sticky Focus for the buffer
  1519.         gdwFocus = DSBCAPS_STICKYFOCUS;
  1520.         lpsz = lpToken;
  1521.         }
  1522.     else if( !lstrcmpi( szToken, gszGlobalToken ))
  1523.         {
  1524.         // Use Global Focus for the buffer
  1525.         gdwFocus = DSBCAPS_GLOBALFOCUS;
  1526.         lpsz = lpToken;
  1527.         }
  1528.     else if( !lstrcmpi( szToken, gszCloseToken ))
  1529.         {
  1530.         // "/close" will cause the program to shutdown after it's done
  1531.         // playing the file that was presumably loaded at the command-line
  1532.         gfCloseOnDone = TRUE;
  1533.         lpsz = lpToken;
  1534.         }
  1535.     else
  1536.         {
  1537.         // Unrecognized parameter followed the slash, so skip over it
  1538.         // and find the next break
  1539.         while( *lpsz && *lpsz != ' ' )
  1540.         lpsz++;
  1541.         }
  1542.     // Clear any spaces out again
  1543.     while( *lpsz == ' ' )
  1544.         lpsz++;
  1545.     }
  1546.  
  1547.     // If that's all that was on the command-line, simply return
  1548.     if( !*lpsz )
  1549.     return;
  1550.  
  1551.     // ASSUMPTION: We assume that a single filename is the only remaining
  1552.     // parameter.  This works out okay because anything else will fail in the
  1553.     // file load inside StreamBufferSetup();
  1554.     lstrcpy( szFileBuffer, lpsz );
  1555.  
  1556.     // Search backwards and find the last backslash, stopping at the
  1557.     // beginning of the file name
  1558.     lpsz = &szFileBuffer[lstrlen(szFileBuffer)];
  1559.  
  1560.     while( lpsz > szFileBuffer )
  1561.     {
  1562.     if( *(lpsz-1) == '\\' )
  1563.         {
  1564.         break;
  1565.         }
  1566.     lpsz--;
  1567.     }
  1568.     // Fake the szFileTitle, which normally gets set by the Common Dialog
  1569.     lstrcpy( szFileTitle, lpsz );
  1570.     lstrcpy( szCDStartPath, szFileBuffer );
  1571.     szCDStartPath[lpsz-szFileBuffer] = 0;
  1572.  
  1573.     if( fStartLooping )
  1574.     {
  1575.     // Allowing auto-close when the user will have plenty of time to click
  1576.     // stop would cause the app to shutdown right as they hit the button,
  1577.     // which is weird behavior.
  1578.     gfCloseOnDone = FALSE;
  1579.     Button_SetCheck( hWndLoopCheck, TRUE );
  1580.     }
  1581.  
  1582.     if( StreamBufferSetup() != 0 )
  1583.     {
  1584.     // Error opening the WAVE file so abort
  1585.     return;
  1586.     }
  1587.     else
  1588.     {
  1589.     bFileOpen = TRUE;
  1590.     EnableMenuItem( GetMenu( hWndMain ), IDM_PLAY,
  1591.             MF_BYCOMMAND | MF_ENABLED );
  1592.     EnableWindow( hWndPlay, TRUE );
  1593.     EnableMenuItem( GetMenu( hWndMain ), IDM_FILE_CLOSE,
  1594.             MF_BYCOMMAND | MF_ENABLED );
  1595.     DrawMenuBar( hWndMain );
  1596.     BuildTitleBarText();
  1597.  
  1598.     if( fStartPlaying )
  1599.         SendMessage( hWndMain, WM_COMMAND, MAKEWPARAM( IDM_PLAY, 0 ), 0L );
  1600.     }
  1601.     }
  1602.  
  1603.  
  1604. // ==========================================================
  1605. // SetupNotifications
  1606. //    Sets notifications and also creates the events. 
  1607. // ==========================================================
  1608. int SetupNotifications(void)
  1609. {
  1610.     int hr = DS_OK;
  1611.     DSBPOSITIONNOTIFY dsbPosNotify[NUM_PLAY_NOTIFICATIONS +1 ];    
  1612.     DWORD dwSize = wiWave.dwNotifySize;    
  1613.     DWORD dwThreadId;
  1614.     int i;
  1615.  
  1616.     // Create the 2 events. One for Play one for stop.
  1617.     hNotifyEvent[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
  1618.     ASSERT(hNotifyEvent[0]);
  1619.     hNotifyEvent[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
  1620.     ASSERT(hNotifyEvent[1]);
  1621.  
  1622.         // setup the first one.    
  1623.     dsbPosNotify[0].dwOffset = dwSize;
  1624.     dsbPosNotify[0].hEventNotify = hNotifyEvent[0];
  1625.     DPF(0, "Set notifies for position %lu", dsbPosNotify[0].dwOffset);
  1626.  
  1627.     for (i = 1; i < NUM_PLAY_NOTIFICATIONS; i++)
  1628.     {
  1629.         dsbPosNotify[i].dwOffset = dsbPosNotify[i-1].dwOffset + dwSize;
  1630.         dsbPosNotify[i].hEventNotify = hNotifyEvent[0];                
  1631.         DPF(0, "Set notifies for positions %lu", dsbPosNotify[i].dwOffset);
  1632.     }
  1633.  
  1634.     dsbPosNotify[i-1].dwOffset -= 1;
  1635.     // set the stop notification.
  1636.     dsbPosNotify[i].dwOffset = DSBPN_OFFSETSTOP;
  1637.     dsbPosNotify[i].hEventNotify = hNotifyEvent[1];
  1638.  
  1639.     // Now create the thread to wait on the events created.
  1640.     if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HandleNotifications, NULL, 0, &dwThreadId) == NULL)
  1641.     {
  1642.         DPF(0, "CreateThread failed");
  1643.         goto HandleErr;
  1644.     }
  1645.     else
  1646.         DPF(0, "Created thread");
  1647.  
  1648.     // setup notification
  1649.     hr = lpDirectSoundNotify->lpVtbl->SetNotificationPositions(lpDirectSoundNotify, 
  1650.                                                                 NUM_PLAY_NOTIFICATIONS +1,
  1651.                                                                 dsbPosNotify);
  1652.  
  1653. HandleErr:
  1654.     if (hr != DS_OK)
  1655.     {    if (hr == DSERR_INVALIDPARAM)
  1656.             DPF(0, "SetupNotificationPos failed. Invalid Param");
  1657.         if (hr == DSERR_OUTOFMEMORY)
  1658.             DPF(0, "SetupNotificationPos failed. OutOfMemory");
  1659.         CloseHandle(hNotifyEvent[0]);
  1660.         CloseHandle(hNotifyEvent[1]);
  1661.         hNotifyEvent[0] = hNotifyEvent[1] = (HANDLE)NULL;
  1662.     }
  1663. #ifdef DEBUG
  1664.     else
  1665.     {
  1666.         DPF(3, "SetNotificationPos succeeded.");
  1667.     }
  1668. #endif
  1669.  
  1670.     return (hr);
  1671. }
  1672.