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