DSSTREAM.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:60k
源码类别:

Windows编程

开发平台:

Visual C++

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