home *** CD-ROM | disk | FTP | other *** search
/ NEXT Generation 27 / NEXT27.iso / pc / demos / emperor / dx3.exe / SDK / SAMPLES / DSSHOW3D / DSSHOW3D.CPP < prev    next >
C/C++ Source or Header  |  1996-08-28  |  41KB  |  1,385 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:       DSShow3d.c
  6.  *  Content:    Direct Sound show-off, including 3D Sound.
  7.  *
  8.  *
  9.  ***************************************************************************/
  10.  
  11. //#pragma warning( disable: 4102 4101 )
  12.  
  13. #define INITGUID
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include <commctrl.h>
  17. #include <commdlg.h>
  18. #include <cguid.h>
  19.  
  20. #include <mmsystem.h>
  21. #include <mmreg.h>
  22. #include <msacm.h>
  23. #include <dsound.h>
  24.  
  25. #include "DSShow3D.h"
  26. #define INIT_GVARS
  27. #include "GVars.h"
  28.  
  29. #include "MainWnd.h"
  30. #include "FileInfo.h"
  31.  
  32. #include "wave.h"
  33. #include "debug.h"
  34.  
  35. static char szOpenStartDir[MAX_PATH];
  36.  
  37. // Format codes used to compactly represent each possible output format
  38. //
  39. // The code is: FFCBB where...
  40. //
  41. //    FF -> Frequency 8=8000, 11=11025, 22=22050, 44=44100
  42. //    C  -> # of channels (1 or 2)
  43. //    BB -> Bits 08 is 8-bit, 16 is 16-bit
  44. //
  45. // Use the FormatCodeToWFX() function to set a WAVEFORMATEX structure
  46. // based on a format code, or FormatCodeToText() to get a text string
  47. // representing the format.
  48. //
  49.  
  50. #define FC_GETFREQCODE(fc)  ((fc) / 1000)
  51. #define FC_GETBITS(fc)      ((fc) % 100)
  52. #define FC_GETCHANNELS(fc)  (((fc) % 1000) / 100)
  53.  
  54. // Functions limited in scope to this file
  55.  
  56. static BOOL InitializeDSound( void );
  57. static void FreeDSound( void );
  58. static BOOL InitInstance( HINSTANCE, LPSTR, int );
  59. static BOOL InitPrimarySoundBuffer( void );
  60. static void GetMediaPath( LPSTR, int );
  61. static BOOL LoadRegistrySettings( void );
  62. static BOOL SaveRegistrySettings( void );
  63. static BOOL ParseCommandLine( LPSTR lpszCmdLine );
  64. static BOOL fMatchToken( PSTR pszString, PSTR pszDatum, int cchLen );
  65. static BOOL fGetToken( PSTR pszStart, PSTR *ppszRet, int *pcchRet );
  66.  
  67.  
  68. /* InitializeDSound()
  69.  *
  70.  *    Initialize the DirectSound object and other stuff we'll use like the
  71.  * primary buffer and 3D listener object.
  72.  */
  73. BOOL InitializeDSound( void )
  74.     {
  75.     HRESULT hr;
  76.  
  77.     hr = CoCreateInstance( CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
  78.                     IID_IDirectSound, (void**)&gpds );
  79.  
  80.     if (FAILED(hr) || (NULL == gpds))
  81.     {
  82.     DPF( 0, "Could not create a DSound object (%s)", TranslateDSError(hr));
  83.     MessageBox( AppWnd.GetHwnd(), "Unable to get a DirectSound object",
  84.                     "COM Failure", MB_OK | MB_ICONSTOP );
  85.     goto ID_ExitError;
  86.     }
  87.     DPF( 2, "Got an IDirectSound object" );
  88.  
  89.     if( grs.fDefaultDevice )
  90.     {
  91.     DPF( 2, "Using default device as first choice" );
  92.     hr = gpds->Initialize( &GUID_NULL );
  93.     }
  94.     else
  95.     {
  96.     if( FAILED( hr = gpds->Initialize( &grs.guPreferredDevice )))
  97.         {
  98.         DPF( 0, "Initialize failed on preferred device, using default" );
  99.         if( IDNO == MessageBox( GetActiveWindow(),
  100.             "Unable to use preferred device. Use default instead?",
  101.             gszAppName, MB_YESNO ))
  102.         {
  103.         DPF( 2, "User chose not to use default device instead" );
  104.         goto ID_ExitReleaseDS;
  105.         }
  106.         else
  107.         {
  108.         DPF( 2, "Falling back to default device" );
  109.         hr = gpds->Initialize( &GUID_NULL );
  110.         }
  111.         }
  112.     }
  113.     if (FAILED(hr))
  114.     {
  115.     DPF( 0, "Failed Initialize() on IDirectSound object (%s)", TranslateDSError(hr));
  116.     MessageBox( AppWnd.GetHwnd(), "Could not initialize DirectSound object",
  117.                     gszAppName, MB_OK | MB_ICONSTOP );
  118.     goto ID_ExitReleaseDS;
  119.     }
  120.  
  121.     if( grs.fUseExclusiveMode )
  122.     {
  123.     hr = gpds->SetCooperativeLevel( AppWnd.GetHwnd(), DSSCL_EXCLUSIVE);
  124.         DPF( 3, "Received DSSCL_EXCLUSIVE" );
  125.     }
  126.     else
  127.     {
  128.     hr = gpds->SetCooperativeLevel( AppWnd.GetHwnd(), DSSCL_PRIORITY);
  129.         DPF( 3, "Received DSSCL_PRIORITY" );
  130.     }
  131.     if (FAILED(hr))
  132.     {
  133.     DPF( 0, "Couldn't SetCooperativeLevel() (%s)", TranslateDSError(hr));
  134.     MessageBox( AppWnd.GetHwnd(), "Could not SetCooperativeLevel()",
  135.                     gszAppName, MB_OK | MB_ICONSTOP );
  136.     goto ID_ExitReleaseDS;
  137.     }
  138.  
  139.     DPF( 3, "Creating Primary Buffer" );
  140.  
  141.     if( !InitPrimarySoundBuffer())
  142.     {
  143.     DPF( 0, "Failed on call to InitPrimarySoundBuffer()" );
  144.     goto ID_ExitReleaseDS;
  145.     }
  146.  
  147.     // Return SUCCESS
  148.     return TRUE;
  149.  
  150. ID_ExitReleaseDS:
  151.     // The InitPrimarySoundBuffer() call should have cleaned up
  152.     // after itself if it failed
  153.  
  154.     ASSERT( NULL == gp3DListener );
  155.     ASSERT( NULL == gpdsbPrimary );
  156.  
  157.     if( NULL != gpds )
  158.     {
  159.     gpds->Release();
  160.     gpds = NULL;
  161.     }
  162.  
  163. ID_ExitError:
  164.     return FALSE;
  165.     }
  166.  
  167.  
  168. void FreeDSound()
  169.     {
  170.     if( NULL != gpdsbPrimary )
  171.     {
  172.     gpdsbPrimary->Stop();
  173.     gpdsbPrimary->Release();
  174.     gpdsbPrimary = NULL;
  175.     }
  176.     if( NULL != gp3DListener )
  177.     {
  178.     gp3DListener->Release();
  179.     gp3DListener = NULL;
  180.     }
  181.     if( NULL != gpds )
  182.     {
  183.     gpds->Release();
  184.     gpds = NULL;
  185.     }
  186.     }
  187.  
  188.  
  189. /* InitInstance()
  190.  *
  191.  *    This function is responsible for all initialization that must occur
  192.  * when a new instance of our application is started.
  193.  */
  194. BOOL InitInstance( HINSTANCE hInstance, LPSTR lpszCommandLine, int cmdShow )
  195.     {
  196.     WNDCLASS    myClass;
  197.  
  198.     myClass.hCursor             = LoadCursor(NULL, IDC_ARROW);
  199.     myClass.hIcon               = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_SPEAKER_RGB_3D));
  200.     myClass.lpszMenuName        = MAKEINTRESOURCE(IDR_MAINMENU);
  201.     myClass.lpszClassName       = (LPSTR)gszAppWndClass;
  202.     myClass.hbrBackground       = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  203.     myClass.hInstance           = hInstance;
  204.     myClass.style               = CS_HREDRAW | CS_VREDRAW;
  205.     myClass.lpfnWndProc         = MainWndProc;
  206.     myClass.cbClsExtra          = 0;
  207.     myClass.cbWndExtra          = 0;
  208.  
  209.     if (!RegisterClass( &myClass ))
  210.        return FALSE;
  211.  
  212.     // Load the current registry settings
  213.     LoadRegistrySettings();
  214.     gdwOutputFormat = grs.dwPreferredFormat;
  215.  
  216.     if( !AppWnd.Create())
  217.     goto II_ExitError;
  218.  
  219.     AppWnd.ShowWindow( cmdShow );
  220.     AppWnd.UpdateWindow();
  221.  
  222.     /* Continue doing other initialization stuff */
  223.  
  224.     // Setup the timer...
  225.     if ((gdwTimer = SetTimer(AppWnd.GetHwnd(), 1, TIMERPERIOD, NULL)) == 0)
  226.     {
  227.     MessageBox(AppWnd.GetHwnd(), "Cannot allocate timer, aborting", gszAppCaption,
  228.             MB_OK|MB_ICONSTOP);
  229.     goto II_ExitError;
  230.     }
  231.  
  232.     // Get the largest waveformatex structure.
  233.     if (MMSYSERR_NOERROR != acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT,
  234.                             &gcbMaxWaveFormatSize))
  235.         {
  236.     MessageBox(AppWnd.GetHwnd(), "ACM Metrics failed, aborting", gszAppCaption,
  237.            MB_OK|MB_ICONSTOP);
  238.     goto II_ExitError;
  239.     }
  240.  
  241.     // Initialize the COM subsystem and create our DirectSound stuff
  242.  
  243.     if (!SUCCEEDED(CoInitialize(NULL)))
  244.     {
  245.     MessageBox( AppWnd.GetHwnd(), "Failed to initialize COM library",
  246.             gszAppCaption, MB_OK | MB_ICONSTOP);
  247.     goto II_ExitError;
  248.     }
  249.     else
  250.     gfCOMInitialized = TRUE;
  251.  
  252.     // Initialize the DirectSound global interfaces
  253.     if( !InitializeDSound())
  254.     goto II_ExitShutdownCOM;
  255.  
  256.     if( !ParseCommandLine( lpszCommandLine ))
  257.     goto II_ExitFreeDSound;
  258.  
  259.     return TRUE;    // TRUE indicates success
  260.  
  261.  
  262. II_ExitFreeDSound:
  263.     FreeDSound();
  264.  
  265. II_ExitShutdownCOM:
  266.     if( gfCOMInitialized )
  267.     {
  268.     DPF( 0, "Calling CoUninitialize from InitInstance error cleanup" );
  269.     CoUninitialize();
  270.     }
  271.  
  272. II_ExitError:
  273.  
  274.     return FALSE;   // FALSE indicates failure on initialization
  275.     }   // InitInstance()
  276.  
  277.  
  278. /* WinMain()
  279.  *
  280.  *   Main entry point for this program's execution.  Everything starts here.
  281.  */
  282. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  283.                         LPSTR lpszCmdLine, int cmdShow)
  284.     {
  285.     MSG   msg;
  286.  
  287.     DbgInitialize( TRUE );
  288.  
  289.     InitCommonControls();
  290.  
  291.     // Save instance handle
  292.     ghInst = hInstance;
  293.  
  294.     if (!InitInstance(hInstance, lpszCmdLine, cmdShow))
  295.     return 0;
  296.  
  297.     /* Polling messages from event queue */
  298.     while (GetMessage((LPMSG)&msg, NULL, 0, 0))
  299.     {
  300.     // Only Translate and Dispatch the message if it's not going
  301.     // to one of our many modeless dialog windows
  302.     if( !IsDialogMessage( ghwndListener, &msg )
  303.         && (NULL == ghDlgActive || !IsDialogMessage( ghDlgActive, &msg )))
  304.         {
  305.         TranslateMessage((LPMSG)&msg);
  306.         DispatchMessage((LPMSG)&msg);
  307.         }
  308.     }
  309.  
  310.     UnregisterClass(gszAppWndClass, hInstance);
  311.     return (int)msg.wParam;
  312.     }   // WinMain()
  313.  
  314.  
  315. /* InitPrimarySoundBuffer()
  316.  *
  317.  *    Creates and initializes the primary sound buffer for the application.
  318.  * We need the primary buffer in order to get the 3D listener interface and
  319.  * also to select output format type.
  320.  */
  321. BOOL InitPrimarySoundBuffer( void )
  322.     {
  323.     HRESULT     hr;
  324.     DSBUFFERDESC    dsbd;
  325.     int         nCurFormat;
  326.  
  327.     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC));
  328.     
  329.     gpwfxFormat = new WAVEFORMATEX;
  330.     if( NULL == gpwfxFormat )
  331.     return FALSE;
  332.  
  333.     ZeroMemory( gpwfxFormat, sizeof(WAVEFORMATEX));
  334.  
  335.     gpwfxFormat->wFormatTag = WAVE_FORMAT_PCM;
  336.  
  337.     FormatCodeToWFX( gdwOutputFormat, gpwfxFormat );
  338.     DPF( 2, "Initial format code: %lu", gdwOutputFormat );
  339.  
  340.     dsbd.dwSize = sizeof(DSBUFFERDESC);
  341.     dsbd.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
  342.  
  343.     if( FAILED( hr = gpds->CreateSoundBuffer( &dsbd, &gpdsbPrimary, NULL )))
  344.     {
  345.     DPF( 0, "Couldn't create primary buffer (%s)", TranslateDSError(hr));
  346.     goto IPSB_ExitError;
  347.     }
  348.  
  349.     if( FAILED( hr = gpdsbPrimary->SetFormat( gpwfxFormat )))
  350.     {
  351.     DisableFormatCode( gdwOutputFormat );
  352.  
  353.     // If we couldn't load the desired format, then try everything starting
  354.     // at really high quality, and degrading to 8M8 (yuck)
  355.     nCurFormat = 0;
  356.     DPF( 2, "Unable to SetFormat() preferred format to %lu", gdwOutputFormat );
  357.  
  358.     while( FAILED( hr ) && nCurFormat < NUM_FORMATENTRIES )
  359.         {
  360.         gdwOutputFormat = aFormatOrder[nCurFormat];
  361.         FormatCodeToWFX( gdwOutputFormat, gpwfxFormat );
  362.         DPF( 2, "Trying format code: %lu", gdwOutputFormat );
  363.  
  364.         hr = gpdsbPrimary->SetFormat( gpwfxFormat );
  365.         if( FAILED(hr))
  366.         {
  367.         DisableFormatCode( gdwOutputFormat );
  368.         DPF( 2, "Return from SetFormat on primary buffer = %s", TranslateDSError(hr));
  369.         }
  370.         else
  371.         {
  372.         EnableFormatCode( gdwOutputFormat );
  373.         DPF( 2, "Succeeded on SetFormat() for code %lu", gdwOutputFormat );
  374.         }
  375.  
  376.         nCurFormat++;
  377.         }
  378.     }
  379.     if( FAILED( hr ))
  380.     {
  381.     DPF( 0, "Failed SetFormat() on all formats!" );
  382.     goto IPSB_ExitError;
  383.     }
  384.  
  385.     if( FAILED( hr = gpdsbPrimary->QueryInterface( IID_IDirectSound3DListener,
  386.                         (void**)&gp3DListener)))
  387.     {
  388.     DPF( 0, "Couldn't QI primary buffer for 3D listener interface (%s)", TranslateDSError(hr));
  389.     goto IPSB_ExitError;
  390.     }
  391.     if( FAILED( hr = gpdsbPrimary->Play( 0, 0, DSBPLAY_LOOPING )))
  392.     {
  393.     DPF( 0, "Couldn't play primary buffer (%s)", TranslateDSError(hr));
  394.     goto IPSB_ExitRelease;
  395.     }
  396.  
  397.     return TRUE;
  398.  
  399. IPSB_ExitRelease:
  400.     if( gp3DListener )
  401.     {
  402.     DPF( 0, "Releasing 3D Listener in InitPrimarySoundBuffer() error cleanup" );
  403.     gp3DListener->Release();
  404.     gp3DListener = NULL;
  405.     }
  406.     if( gpdsbPrimary )
  407.     {
  408.     DPF( 0, "Releasing Primary in InitPrimarySoundBuffer() error cleanup" );
  409.     gpdsbPrimary->Stop();
  410.     gpdsbPrimary->Release();
  411.     gpdsbPrimary = NULL;
  412.     }
  413.  
  414. IPSB_ExitError:
  415.     return FALSE;
  416.     }
  417.  
  418.  
  419. /*  This will pop up the open file dialog and allow the user to pick one file.
  420.  
  421.     Input:
  422.     hWnd        -   Handle of parent window.
  423.     pszFileName -   String to store filename in, must be >= MAX_PATH long.
  424.  
  425.  
  426.     Output:
  427.     TRUE if a file was picked successfully or FALSE if the user didn't
  428.     pick a file)
  429.  
  430.  */
  431. BOOL OpenFileDialog( HWND hWnd, LPSTR pszFileName,
  432.                     int *nFileName, LPDWORD lpdwFlags )
  433.     {
  434.     BOOL            fReturn,
  435.             fValid;
  436.     OPENFILENAME    ofn;
  437.  
  438.     pszFileName[0]          = 0;
  439.  
  440.     ofn.lStructSize         = sizeof(ofn);
  441.     ofn.hwndOwner           = hWnd;
  442.     ofn.hInstance           = ghInst;
  443.     ofn.lpstrFilter         = "Wave Files\0*.wav\0All Files\0*.*\0\0";
  444.     ofn.lpstrCustomFilter   = NULL;
  445.     ofn.nMaxCustFilter      = 0;
  446.     ofn.nFilterIndex        = 1;
  447.     ofn.lpstrFile           = pszFileName;
  448.     ofn.nMaxFile            = MAX_PATH;
  449.     ofn.lpstrFileTitle      = NULL;
  450.     ofn.nMaxFileTitle       = 0;
  451.     ofn.lpstrInitialDir     = grs.szInitialDir;
  452.     ofn.lpstrTitle          = "File Open";
  453.     ofn.Flags               = OFN_FILEMUSTEXIST | OFN_EXPLORER
  454.                                 | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK
  455.                 | OFN_HIDEREADONLY;
  456.     ofn.nFileOffset         = 0;
  457.     ofn.nFileExtension      = 0;
  458.     ofn.lpstrDefExt         = "wav";
  459.     ofn.lCustData           = (LONG)lpdwFlags;
  460.     ofn.lpfnHook            = FileOpenCustomTemplateDlgProc;
  461.     ofn.lpTemplateName      = MAKEINTRESOURCE(IDD_FILEOPEN_NEST);
  462.  
  463.     fValid = FALSE;
  464.     do
  465.     {
  466.     if( fReturn = GetOpenFileName( &ofn ))
  467.     {
  468.     fValid = IsValidWave( pszFileName );
  469.     if( !fValid )
  470.     MessageBox( hWnd, "Wave files must be PCM format!",
  471.                 "Invalid Wave File", MB_OK|MB_ICONSTOP );
  472.     else
  473.     *nFileName = ofn.nFileOffset;
  474.     }
  475.     else
  476.     fValid = TRUE;         // Force break out of loop.
  477.     } while( !fValid );
  478.  
  479.     return fReturn;
  480.     }
  481.  
  482. /*
  483.  *
  484.  *
  485. This function will determine if the filename passed
  486.     in is a valid wave for this
  487.     app, that is a PCM wave.
  488.  
  489.     Input:
  490.     pszFileName -   FileName to check.
  491.  
  492.     Output:
  493.     FALSE if not a valid wave, TRUE if it is.
  494.  
  495. */
  496. BOOL IsValidWave( LPSTR pszFileName )
  497.     {
  498.     BOOL        fReturn = FALSE;
  499.     int             nError = 0;
  500.     HMMIO           hmmio;
  501.     MMCKINFO        mmck;
  502.     WAVEFORMATEX    *pwfx;
  503.  
  504.     if ((nError = WaveOpenFile(pszFileName, &hmmio, &pwfx, &mmck)) != 0)
  505.     {
  506.     goto ERROR_IN_ROUTINE;
  507.     }
  508.  
  509.     if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
  510.     {
  511.     goto ERROR_IN_ROUTINE;
  512.     }
  513.  
  514.     WaveCloseReadFile(&hmmio, &pwfx);
  515.  
  516.     fReturn = TRUE;
  517.  
  518. ERROR_IN_ROUTINE:
  519.     return fReturn;
  520.     }
  521.  
  522.  
  523. /* AboutDlgProc()
  524.  *
  525.  *    Standard dialog procedure for the About box.  As simple as can be.
  526.  */
  527. BOOL CALLBACK AboutDlgProc( HWND hWnd, UINT uMsg,
  528.                         WPARAM wParam, LPARAM lParam )
  529.     {
  530.     switch(uMsg)
  531.     {
  532.     case WM_INITDIALOG:
  533.         break;
  534.  
  535.     case WM_COMMAND:
  536.         switch(wParam)
  537.         {
  538.         case ID_OK:
  539.         PostMessage(hWnd, WM_CLOSE, 0, 0);
  540.         break;
  541.  
  542.         default:
  543.         break;
  544.  
  545.         }
  546.         break;
  547.  
  548.     case WM_CLOSE:
  549.         EndDialog(hWnd, 0);
  550.         break;
  551.  
  552.     default:
  553.         return FALSE;
  554.         break;
  555.  
  556.     }
  557.  
  558.     return TRUE;
  559. }
  560.  
  561.  
  562. /* FileOpenCustomTemplateDlgProc()
  563.  *
  564.  * This "hook procedure" is called by the common dialog code for certain
  565.  *   events that may occur during the life of our nested dialog structure.
  566.  *   We nest the Explorer style dialog inside our file open dialog so we
  567.  *   can add a check box for stick buffers.
  568.  */
  569. UINT CALLBACK FileOpenCustomTemplateDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
  570.     {
  571.     static LPOPENFILENAME   lpofn = NULL;
  572.     static HWND         hStickyRadio, h3DCheck, hLocalRadio, hGlobalRadio;
  573.     static HWND         hGetPosRadio, hGetPos2Radio;
  574.  
  575.     switch( message )
  576.     {
  577.     case WM_INITDIALOG:
  578.         lpofn = (LPOPENFILENAME)lParam;
  579.  
  580.     h3DCheck = GetDlgItem( hDlg, IDC_FONEST_3D );
  581.     hLocalRadio = GetDlgItem( hDlg, IDC_FONEST_LOCAL_RADIO );
  582.         hStickyRadio = GetDlgItem( hDlg, IDC_FONEST_STICKY_RADIO );
  583.     hGlobalRadio = GetDlgItem( hDlg, IDC_FONEST_GLOBAL_RADIO );
  584.     hGetPosRadio = GetDlgItem( hDlg, IDC_FONEST_GETPOS_RADIO );
  585.     hGetPos2Radio = GetDlgItem( hDlg, IDC_FONEST_GETPOS2_RADIO );
  586.  
  587.     Button_SetCheck( hGetPos2Radio, TRUE );
  588.  
  589.     if( grs.dwDefaultFocusFlag & DSBCAPS_STICKYFOCUS )
  590.         Button_SetCheck( hStickyRadio, TRUE );
  591.     else if( grs.dwDefaultFocusFlag & DSBCAPS_GLOBALFOCUS )
  592.         Button_SetCheck( hGlobalRadio, TRUE );
  593.     else
  594.         {
  595.         ASSERT( grs.dwDefaultFocusFlag == 0 );
  596.         Button_SetCheck( hLocalRadio, TRUE );
  597.         }
  598.  
  599.     Button_SetCheck( h3DCheck, grs.fOpen3D );
  600.  
  601.     *((LPDWORD)lpofn->lCustData) = 0;
  602.  
  603.         return TRUE;
  604.  
  605.  
  606.     case WM_NOTIFY:
  607.         switch(((LPOFNOTIFY)lParam)->hdr.code)
  608.         {
  609.         case CDN_SELCHANGE:
  610.             /* Use this area to process anything that must be updated when the
  611.              * user changes the selection in the Common Dialog Box.
  612.              *   NOTE: Provided only for informational purposes
  613.              */
  614.             return FALSE;
  615.  
  616.         case CDN_FILEOK:
  617.             /* We can do lots of things in this notification message.  The most
  618.              * important is that we can decide whether the Common Dialog call
  619.              * will go through or whether it will fail.  I decided to handle
  620.              * the checkbox control in this one place versus 4 others...
  621.              */
  622.             ASSERT( lpofn != NULL );
  623.         /* Set flags to match the current state of the check box controls */
  624.  
  625.         /* Use normal GetPosition */
  626.         *((LPDWORD)lpofn->lCustData) |=
  627.         Button_GetCheck(hGetPosRadio)? OPENFILENAME_F_GETPOS : 0;
  628.         /* Use DSBCAPS_GETCURRENTPOSITION2 */
  629.         *((LPDWORD)lpofn->lCustData) |=
  630.         Button_GetCheck(hGetPos2Radio)? OPENFILENAME_F_GETPOS2 : 0;
  631.  
  632.         /* Local buffer focus */
  633.         *((LPDWORD)lpofn->lCustData) |=
  634.         Button_GetCheck(hStickyRadio)? OPENFILENAME_F_LOCALFOCUS : 0;
  635.         /* Sticky buffer focus */
  636.         *((LPDWORD)lpofn->lCustData) |=
  637.         Button_GetCheck(hStickyRadio)? OPENFILENAME_F_STICKYFOCUS : 0;
  638.         /* Global sound focus */
  639.         *((LPDWORD)lpofn->lCustData) |=
  640.         Button_GetCheck(hGlobalRadio)? OPENFILENAME_F_GLOBALFOCUS : 0;
  641.         /* 3D Buffer */
  642.         *((LPDWORD)lpofn->lCustData) |=
  643.             Button_GetCheck(h3DCheck)? OPENFILENAME_F_3D : 0;
  644.             /* Returning zero signifies that we "approve" of the OK command,
  645.              * and allows the common dialog to finish.
  646.              */
  647.             return FALSE;
  648.         }
  649.         /* Let the default dialog do/continue processing */
  650.         return FALSE;
  651.     }
  652.     return FALSE;
  653. }
  654.  
  655.  
  656. /* SettingsDlgProc()
  657.  *
  658.  *    DialogBox procedure for the Settings dialog, which sets a bunch of
  659.  * the application-global settings stored in our global REGSETTINGS struct.
  660.  */
  661. BOOL CALLBACK SettingsDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
  662.     {
  663.     static BOOL fSettingsSaved;
  664.     static HWND hInitialDirEdit, hDeviceCombo, hDeviceText, hDefaultCheck;
  665.     static HWND hFormatCombo, hExclusiveCheck, hOpen3DCheck, hFocusCombo;
  666.  
  667.     int     ndx, i, idxLocal, idxSticky, idxGlobal;
  668.     char    szFormat[32];
  669.     LPGUID  lpguTemp;
  670.  
  671.     switch( message )
  672.     {
  673.     case WM_INITDIALOG:
  674.         hInitialDirEdit = GetDlgItem( hDlg, IDC_SETTINGS_INITIALDIR_EDIT );
  675.         hDeviceCombo = GetDlgItem( hDlg, IDC_SETTINGS_DSD_DEVICE_COMBO );
  676.         hFormatCombo = GetDlgItem( hDlg, IDC_SETTINGS_DSD_FORMAT_COMBO );
  677.         hFocusCombo = GetDlgItem( hDlg, IDC_SETTINGS_FOCUS_COMBO );
  678.         hDeviceText = GetDlgItem( hDlg, IDC_SETTINGS_DSD_DEVICE_TEXT );
  679.         hDefaultCheck = GetDlgItem( hDlg, IDC_SETTINGS_DSD_DEFAULT_CHECK );
  680.         hExclusiveCheck = GetDlgItem( hDlg, IDC_SETTINGS_EXCLUSIVE_CHECK );
  681.         hOpen3DCheck = GetDlgItem( hDlg, IDC_SETTINGS_OPEN3D_CHECK );
  682.  
  683.         // We use this flag to determine the return value from DialogBox()
  684.         // FALSE indicates no change, TRUE means a change occurred.
  685.         fSettingsSaved = FALSE;
  686.  
  687.         // Add all the format strings to the combo box
  688.         for( i = 0; i < NUM_FORMATENTRIES; i ++ )
  689.         {
  690.         // Only add formats that are available on this card (this will
  691.         // ignore any formats we couldn't SetFormat() with at startup,
  692.         // and any we have since found to be unusable as well).
  693.         if( !fdFormats[i].fEnable )
  694.             continue;
  695.  
  696.         if( FormatCodeToText(fdFormats[i].dwCode, szFormat, sizeof(szFormat)))
  697.             {
  698.             ndx = ComboBox_AddString( hFormatCombo, szFormat );
  699.             ComboBox_SetItemData( hFormatCombo, ndx, fdFormats[i].dwCode );
  700.             }
  701.         }
  702.  
  703.         DirectSoundEnumerate( (LPDSENUMCALLBACK)DSEnumProc, (LPVOID)&hDeviceCombo );
  704.  
  705.         // Add the three focus choices to the listbox, and set the item data
  706.         // for each to the appropriate flag
  707.         idxLocal = ComboBox_AddString( hFocusCombo, "Local" );
  708.         ComboBox_SetItemData( hFocusCombo, idxLocal, 0 );
  709.         idxSticky = ComboBox_AddString( hFocusCombo, "Sticky" );
  710.         ComboBox_SetItemData( hFocusCombo, idxSticky, DSBCAPS_STICKYFOCUS );
  711.         idxGlobal = ComboBox_AddString( hFocusCombo, "Global" );
  712.         ComboBox_SetItemData( hFocusCombo, idxGlobal, DSBCAPS_GLOBALFOCUS );
  713.  
  714.         // Select the proper Drop List item
  715.         if( grs.dwDefaultFocusFlag == DSBCAPS_STICKYFOCUS )
  716.         ComboBox_SetCurSel( hFocusCombo, idxSticky );
  717.         else if( grs.dwDefaultFocusFlag == DSBCAPS_GLOBALFOCUS )
  718.         ComboBox_SetCurSel( hFocusCombo, idxGlobal );
  719.         else
  720.         ComboBox_SetCurSel( hFocusCombo, idxLocal );
  721.  
  722.         // Set the states of checkboxes and controls that depend on them
  723.         Button_SetCheck( hDefaultCheck, grs.fDefaultDevice );
  724.         Button_SetCheck( hExclusiveCheck, grs.fUseExclusiveMode );
  725.         Button_SetCheck( hOpen3DCheck, grs.fOpen3D );
  726.         Static_Enable( hDeviceCombo, !grs.fDefaultDevice );
  727.         Static_Enable( hDeviceText, !grs.fDefaultDevice );
  728.  
  729.         Edit_LimitText( hInitialDirEdit, sizeof(grs.szInitialDir));
  730.         Edit_SetText( hInitialDirEdit, grs.szInitialDir );
  731.  
  732.         if (FormatCodeToText(grs.dwPreferredFormat, szFormat, sizeof(szFormat)))
  733.         {
  734.         ComboBox_SetCurSel( hFormatCombo, ComboBox_FindString( hFormatCombo,
  735.                                     -1, szFormat ));
  736.         }
  737.  
  738.         return TRUE;
  739.  
  740.     case WM_COMMAND:
  741.         switch( LOWORD(wParam))
  742.         {
  743.         case IDC_SETTINGS_DSD_DEFAULT_CHECK:
  744.             grs.fDefaultDevice = !grs.fDefaultDevice;
  745.             Static_Enable( hDeviceCombo, !grs.fDefaultDevice );
  746.             Static_Enable( hDeviceText, !grs.fDefaultDevice );
  747.             // By selecting the first item, we wipe out the state where
  748.             // the user could come in with the box checked, uncheck it,
  749.             // and leave without selecting anything
  750.             ComboBox_SetCurSel( hDeviceCombo, 0 );
  751.             break;
  752.  
  753.         case IDC_SETTINGS_BROWSE_INITIALDIR_BUTTON:
  754.             {
  755.             OPENFILENAME    ofn;
  756.  
  757.             ZeroMemory( &ofn, sizeof(ofn));
  758.             // Fill in the ofn structure and do the Common Dialog call
  759.             }
  760.             break;
  761.  
  762.         case IDOK:
  763.             grs.fDefaultDevice = Button_GetCheck( hDefaultCheck );
  764.             if( !grs.fDefaultDevice )
  765.             {
  766.             lpguTemp = (LPGUID)ComboBox_GetItemData( hDeviceCombo,
  767.                         ComboBox_GetCurSel( hDeviceCombo ));
  768.             if( NULL != lpguTemp )
  769.                 grs.guPreferredDevice = *lpguTemp;
  770.             else
  771.                 grs.guPreferredDevice = GUID_NULL;
  772.             }
  773.             else
  774.             grs.guPreferredDevice = GUID_NULL;
  775.  
  776.             grs.fOpen3D = Button_GetCheck( hOpen3DCheck );
  777.             grs.fUseExclusiveMode = Button_GetCheck( hExclusiveCheck );
  778.             grs.dwPreferredFormat = ComboBox_GetItemData( hFormatCombo,
  779.                         ComboBox_GetCurSel( hFormatCombo ));
  780.             grs.dwDefaultFocusFlag = ComboBox_GetItemData( hFocusCombo,
  781.                         ComboBox_GetCurSel( hFocusCombo ));
  782.             Edit_GetText( hInitialDirEdit, grs.szInitialDir,
  783.                         sizeof(grs.szInitialDir));
  784.  
  785.             SaveRegistrySettings();
  786.             fSettingsSaved = TRUE;
  787.             SendMessage( hDlg, WM_CLOSE, 0, 0 );
  788.             break;
  789.  
  790.         case IDCANCEL:
  791.             fSettingsSaved = FALSE;
  792.             SendMessage( hDlg, WM_CLOSE, 0, 0 );
  793.             break;
  794.  
  795.         default:
  796.             return FALSE;
  797.         }
  798.         return TRUE;
  799.         break;
  800.  
  801.     case WM_CLOSE:
  802.         EndDialog( hDlg, fSettingsSaved );
  803.         return TRUE;
  804.  
  805.     case WM_DESTROY:
  806.         while( ComboBox_GetCount( hDeviceCombo ))
  807.         {
  808.         lpguTemp = (LPGUID)ComboBox_GetItemData( hDeviceCombo, 0 );
  809.         if( NULL != lpguTemp )
  810.             delete lpguTemp;
  811.         ComboBox_DeleteString( hDeviceCombo, 0 );
  812.         }
  813.         return TRUE;
  814.  
  815.     default:
  816.         return FALSE;
  817.     }
  818.     ASSERT( FALSE );
  819.     return FALSE;
  820.     }
  821.  
  822.  
  823. /* LoadRegistrySettings()
  824.  *
  825.  *    Load application global REGSETTINGS structure values from the registry.
  826.  */
  827. BOOL LoadRegistrySettings( void )
  828.     {
  829.     HKEY    hReg;
  830.     DWORD   dwVal;
  831.     DWORD   cbValSize;
  832.     BOOL    fRet = TRUE;
  833.  
  834.     // Load current settings from our registry key
  835.     if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_CURRENT_USER, REG_SETTINGS_KEY,
  836.                     0, KEY_READ, &hReg ))
  837.     {
  838.     GetMediaPath( grs.szInitialDir, sizeof(grs.szInitialDir));
  839.     
  840.     grs.fDefaultDevice = TRUE;
  841.     grs.fUseExclusiveMode = FALSE;
  842.     grs.fOpen3D = FALSE;
  843.     grs.dwDefaultFocusFlag = 0;
  844.     grs.szInitialDir[0] = '\0'; 
  845.     grs.dwPreferredFormat = aFormatOrder[0];
  846.     fRet = TRUE;
  847.     goto LRS_Return;
  848.     }
  849.  
  850.     // Load the "Use DirectSound Default Device" flag
  851.     cbValSize = sizeof(dwVal);
  852.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_DEVICE_DEFAULT,
  853.                         NULL, NULL, (LPBYTE)&dwVal,
  854.                         &cbValSize ))
  855.     {
  856.     grs.fDefaultDevice = TRUE;
  857.     fRet = FALSE;
  858.     }
  859.     else
  860.     {
  861.     grs.fDefaultDevice = (BOOL)dwVal;
  862.  
  863.     if( !grs.fDefaultDevice )
  864.         {
  865.         // Load the GUID for the preferred device (only if it's not the default)
  866.         cbValSize = sizeof(grs.guPreferredDevice);
  867.         if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_DEVICE_GUID,
  868.                             NULL, NULL,
  869.                             (LPBYTE)&grs.guPreferredDevice,
  870.                             &cbValSize ))
  871.         {
  872.         // Copy GUID_NULL into the guPreferredDevice (only works in C++)
  873.         grs.guPreferredDevice = GUID_NULL;
  874.         fRet = FALSE;
  875.         }
  876.         }
  877.     }
  878.  
  879.     cbValSize = sizeof(dwVal);
  880.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_USE_EXCLUSIVE,
  881.                         NULL, NULL, (LPBYTE)&dwVal,
  882.                         &cbValSize ))
  883.     {
  884.     grs.fUseExclusiveMode = FALSE;
  885.     fRet = FALSE;
  886.     }
  887.     else
  888.     grs.fUseExclusiveMode = (BOOL)dwVal;
  889.  
  890.     // Load the flag telling sus whether to default to 2D or 3D
  891.     cbValSize = sizeof(dwVal);
  892.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_OPEN3D,
  893.                         NULL, NULL, (LPBYTE)&dwVal,
  894.                         &cbValSize ))
  895.     {
  896.     grs.fOpen3D = FALSE;
  897.     fRet = FALSE;
  898.     }
  899.     else
  900.     grs.fOpen3D = (BOOL)dwVal;
  901.  
  902.     // Load the coded version of the preferred output format
  903.     cbValSize = sizeof(grs.dwPreferredFormat);
  904.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_OUTPUT_FORMAT,
  905.                         NULL, NULL,
  906.                         (LPBYTE)&grs.dwPreferredFormat,
  907.                         &cbValSize ))
  908.     {
  909.     grs.dwPreferredFormat = aFormatOrder[0];
  910.     fRet = FALSE;
  911.     }
  912.  
  913.     // Load the default focus DSBCAPS flags
  914.     cbValSize = sizeof(grs.dwDefaultFocusFlag);
  915.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_FOCUS_FLAG,
  916.                         NULL, NULL,
  917.                         (LPBYTE)&grs.dwDefaultFocusFlag,
  918.                         &cbValSize ))
  919.     {
  920.     grs.dwDefaultFocusFlag = 0;
  921.     fRet = FALSE;
  922.     }
  923.  
  924.     // Load the initial directory for WAVE files
  925.     cbValSize = sizeof(grs.szInitialDir);
  926.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_SETTING_INITIAL_DIR,
  927.                         NULL, NULL,
  928.                         (LPBYTE)&grs.szInitialDir,
  929.                         &cbValSize ))
  930.     {
  931.     GetMediaPath( grs.szInitialDir, sizeof(grs.szInitialDir));
  932.     fRet = FALSE;
  933.     }
  934.  
  935.     if( hReg != NULL )
  936.     {
  937.     RegCloseKey( hReg );
  938.     hReg = NULL;
  939.     }
  940.  
  941. LRS_Return:
  942.     return fRet;
  943.     }
  944.  
  945.  
  946. /* SaveRegistrySettings()
  947.  *
  948.  *    Write the values in the REGSETTINGS global structure to the registry.
  949.  */
  950. BOOL SaveRegistrySettings( void )
  951.     {
  952.     HKEY    hReg;
  953.     DWORD   dwVal, dwCreateDisposition;
  954.     BOOL    fRet = TRUE;
  955.  
  956.     // Save current settings to our registry key
  957.     if( ERROR_SUCCESS != RegCreateKeyEx( HKEY_CURRENT_USER, REG_SETTINGS_KEY,
  958.                     0, NULL, 0, KEY_WRITE, NULL, &hReg,
  959.                     &dwCreateDisposition ))
  960.     {
  961.     fRet = FALSE;
  962.     goto SRS_Return;
  963.     }
  964.  
  965.     // Save the "Use DirectSound Default Device" flag
  966.     dwVal = (DWORD)grs.fDefaultDevice;
  967.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_DEVICE_DEFAULT,
  968.                         0, REG_DWORD, (LPBYTE)&dwVal,
  969.                         sizeof(DWORD)))
  970.     {
  971.     fRet = FALSE;
  972.     }
  973.     else
  974.     {
  975.     if( !grs.fDefaultDevice )
  976.         {
  977.         // Save the GUID for the preferred device (only if it's not the default)
  978.         if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_DEVICE_GUID,
  979.                             0, REG_BINARY,
  980.                             (LPBYTE)&grs.guPreferredDevice,
  981.                             sizeof(GUID)))
  982.         {
  983.         fRet = FALSE;
  984.         }
  985.         }
  986.     }
  987.  
  988.     // Use DSSCL_EXCLUSIVE ??
  989.     dwVal = (DWORD)grs.fUseExclusiveMode;
  990.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_USE_EXCLUSIVE,
  991.                         0, REG_DWORD, (LPBYTE)&dwVal,
  992.                         sizeof(DWORD)))
  993.     {
  994.     fRet = FALSE;
  995.     }
  996.  
  997.     // Open in 3D by default ?
  998.     dwVal = (DWORD)grs.fOpen3D;
  999.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_OPEN3D,
  1000.                         0, REG_DWORD, (LPBYTE)&dwVal,
  1001.                         sizeof(DWORD)))
  1002.     {
  1003.     fRet = FALSE;
  1004.     }
  1005.  
  1006.     // Save the coded version of the preferred output format
  1007.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_OUTPUT_FORMAT,
  1008.                         0, REG_DWORD,
  1009.                         (LPBYTE)&grs.dwPreferredFormat,
  1010.                         sizeof(DWORD)))
  1011.     {
  1012.     fRet = FALSE;
  1013.     }
  1014.  
  1015.     // Save the coded version of the preferred output format
  1016.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_FOCUS_FLAG,
  1017.                         0, REG_DWORD,
  1018.                         (LPBYTE)&grs.dwDefaultFocusFlag,
  1019.                         sizeof(DWORD)))
  1020.     {
  1021.     fRet = FALSE;
  1022.     }
  1023.  
  1024.     // Save the initial directory for WAVE files
  1025.     if( ERROR_SUCCESS != RegSetValueEx( hReg, REG_SETTING_INITIAL_DIR,
  1026.                         0, REG_SZ,
  1027.                         (LPBYTE)&grs.szInitialDir,
  1028.                         lstrlen(grs.szInitialDir)))
  1029.     {
  1030.     fRet = FALSE;
  1031.     }
  1032.  
  1033.     RegCloseKey( hReg );
  1034.     hReg = NULL;
  1035.  
  1036. SRS_Return:
  1037.     return fRet;
  1038.     }
  1039.  
  1040.  
  1041. /* GetMediaPath()
  1042.  *
  1043.  *    In the absence of a registry value, this function is called to pick a
  1044.  * starting point for the File|Open dialog.  Usually \DXSDK\SDK\MEDIA.
  1045.  */
  1046. void GetMediaPath( LPSTR lpszBuf, int nBuf )
  1047.     {
  1048.     HKEY    hReg;
  1049.     DWORD   cbStartPathLen;
  1050.  
  1051.     if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1052.                     REG_DIRECT3D_KEY,
  1053.                     0, KEY_READ, &hReg ))
  1054.     {
  1055.         goto REG_OPEN_FAILED;
  1056.     }
  1057.     else
  1058.     {
  1059.     // Query the Registry for the path to the media directory
  1060.     cbStartPathLen = sizeof( szOpenStartDir );
  1061.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, REG_D3DPATH_VAL,
  1062.                         NULL, NULL,
  1063.                         (LPBYTE)szOpenStartDir,
  1064.                         &cbStartPathLen ))
  1065.         {
  1066.         goto REG_OPEN_FAILED;
  1067.         }
  1068.     RegCloseKey( hReg );
  1069.     hReg = NULL;
  1070.     }
  1071.  
  1072.     return;
  1073.  
  1074. REG_OPEN_FAILED:
  1075.     // Start off by getting the Windows directory -- we're trying to build a
  1076.     // file path like "C:\WINDOWS\MEDIA", but the WINDOWS directory could be
  1077.     // named anything, so we must ask.
  1078.     GetWindowsDirectory( szOpenStartDir, sizeof(szOpenStartDir));
  1079.     // If there's no trailing backslash, append one
  1080.     if( lstrcmp( &szOpenStartDir[lstrlen(szOpenStartDir)], TEXT("\\") ))
  1081.     lstrcat( szOpenStartDir, TEXT("\\"));
  1082.     // Now add on the MEDIA part of the path
  1083.     lstrcat( szOpenStartDir, TEXT("MEDIA"));
  1084.     }
  1085.  
  1086.  
  1087. /* FormatCodeToText()
  1088.  *
  1089.  *    This function reads format codes and puts out a text string for them.
  1090.  * It does not check for invalid codes.  FALSE return means the buffer was
  1091.  * invalid in some way, TRUE means success.
  1092.  *
  1093.  */
  1094. BOOL FormatCodeToText( DWORD dwFormat, LPSTR lpszBuf, int nBufSize )
  1095.     {
  1096.     DWORD   dwFreq;
  1097.  
  1098.     // The longest string we'll ever put in is 21 characters (including NULL)
  1099.     if( NULL == lpszBuf || nBufSize < 21 )
  1100.     return FALSE;
  1101.  
  1102.     // Extract the sample rate
  1103.     dwFreq = FC_GETFREQCODE(dwFormat);
  1104.     dwFreq = ( dwFreq == 8 ? 8000 : (dwFreq / 11) * 11025);
  1105.  
  1106.     wsprintf( lpszBuf, "%u Hz, %u-bit %s", dwFreq, FC_GETBITS(dwFormat),
  1107.         FC_GETCHANNELS(dwFormat) == 1 ? "Mono" : "Stereo" );
  1108.  
  1109.     return TRUE;
  1110.     }
  1111.  
  1112.  
  1113. /* FormatCodeToWFX()
  1114.  *
  1115.  *    This function reads format codes and fills most of the fields of a
  1116.  * WAVEFORMATEX structure based on the values read.  It does not fill the
  1117.  * wFormatTag or cbSize members.
  1118.  *
  1119.  */
  1120. BOOL FormatCodeToWFX( DWORD dwFormat, PWAVEFORMATEX pwfx )
  1121.     {
  1122.     DWORD   dwFreq;
  1123.  
  1124.     if( NULL == pwfx )
  1125.     return FALSE;
  1126.  
  1127.     // Extract the sample rate
  1128.     dwFreq = FC_GETFREQCODE(dwFormat);
  1129.     pwfx->nSamplesPerSec = ( dwFreq == 8 ? 8000 : (dwFreq / 11) * 11025);
  1130.  
  1131.     pwfx->wBitsPerSample = (WORD)FC_GETBITS(dwFormat);
  1132.     pwfx->nChannels = (WORD)FC_GETCHANNELS(dwFormat);
  1133.  
  1134.     // The nBlockAlign calculation below only works for whole-byte samples
  1135.     ASSERT( pwfx->wBitsPerSample % 8 == 0 );
  1136.  
  1137.     pwfx->nBlockAlign = pwfx->nChannels * (pwfx->wBitsPerSample / 8);
  1138.     pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
  1139.  
  1140.     return TRUE;
  1141.     }
  1142.  
  1143.  
  1144. /* FormatCodeFromCommandID()
  1145.  *
  1146.  *    Returns the Format Code that matches the given command ID.
  1147.  */
  1148. DWORD FormatCodeFromCommandID( WORD wID )
  1149.     {
  1150.     int i;
  1151.  
  1152.     for( i = 0; i < NUM_FORMATENTRIES; i++ )
  1153.     {
  1154.     if( fdFormats[i].wCommandID == wID )
  1155.         return fdFormats[i].dwCode;
  1156.     }
  1157.     return 0;
  1158.     }
  1159.  
  1160.  
  1161. /* CommandIDFromFormatCode()
  1162.  *
  1163.  *    Searchs our FORMATDATA array and returns the Command ID corresponding
  1164.  * to the given format code, or 0 if there is no such valid format code.
  1165.  */
  1166. WORD CommandIDFromFormatCode( DWORD dwCode )
  1167.     {
  1168.     int i;
  1169.  
  1170.     for( i = 0; i < NUM_FORMATENTRIES; i++ )
  1171.     {
  1172.     if( fdFormats[i].dwCode == dwCode )
  1173.         return fdFormats[i].wCommandID;
  1174.     }
  1175.     return 0;
  1176.     }
  1177.  
  1178.  
  1179. void DisableFormatCode( DWORD dwCode )
  1180.     {
  1181.     int i;
  1182.  
  1183.     for( i = 0; i < NUM_FORMATENTRIES; i++ )
  1184.     {
  1185.     if( fdFormats[i].dwCode == dwCode )
  1186.         {
  1187.         fdFormats[i].fEnable = FALSE;
  1188.         break;
  1189.         }
  1190.     }
  1191.     }
  1192.  
  1193.  
  1194. void EnableFormatCode( DWORD dwCode )
  1195.     {
  1196.     int i;
  1197.  
  1198.     for( i = 0; i < NUM_FORMATENTRIES; i++ )
  1199.     {
  1200.     if( fdFormats[i].dwCode == dwCode )
  1201.         {
  1202.         fdFormats[i].fEnable = TRUE;
  1203.         break;
  1204.         }
  1205.     }
  1206.     }
  1207.  
  1208.  
  1209. /* DSEnumProc()
  1210.  *
  1211.  *   DirectSoundEnumerate() callback procedure which fills a combo box with
  1212.  * the description strings of all devices and attachs a pointer to a GUID,
  1213.  * which must be freed later by calling delete.
  1214.  */
  1215. BOOL CALLBACK DSEnumProc( LPGUID lpguDevice, LPSTR lpszDesc,
  1216.                                 LPSTR lpszDrvName, LPVOID lpContext )
  1217.     {
  1218.     HWND   hCombo = *(HWND *)lpContext;
  1219.     LPGUID lpguTemp = NULL;
  1220.     int     idx;
  1221.  
  1222.     if( NULL != lpguDevice )
  1223.         {
  1224.         lpguTemp = new GUID;
  1225.     // We failed to allocate storage, so continue with next device
  1226.     if( NULL == lpguTemp )
  1227.         return( TRUE );
  1228.  
  1229.         CopyMemory( lpguTemp, lpguDevice, sizeof(GUID));
  1230.     }
  1231.  
  1232.     idx = ComboBox_AddString( hCombo, lpszDesc );
  1233.     ComboBox_SetItemData( hCombo,
  1234.                 ComboBox_FindString( hCombo, 0, lpszDesc ),
  1235.                 lpguTemp );
  1236.  
  1237.     if( !grs.fDefaultDevice )
  1238.     {
  1239.     if( NULL == lpguTemp )
  1240.         {
  1241.         if( grs.guPreferredDevice == GUID_NULL )
  1242.         ComboBox_SetCurSel( hCombo, idx );
  1243.         }
  1244.     else if( *lpguTemp == grs.guPreferredDevice )
  1245.         ComboBox_SetCurSel( hCombo, idx );
  1246.     }
  1247.     
  1248.     return( TRUE );
  1249.     }
  1250.  
  1251.  
  1252. /* fGetToken()
  1253.  *
  1254.  *    Parses the command-line string "in place" starting at pszStart.  A ptr
  1255.  * to the start of the next token and it's length will be the out parameters,
  1256.  * or NULL and 0 if no token.  Note that *ppszRet will NOT be NULL-terminated
  1257.  * since the string is part of another string.  That's what then length is for.
  1258.  *
  1259.  * Returns: TRUE if a token was retrieved, or FALSE if there was no token.
  1260.  */
  1261. BOOL fGetToken( PSTR pszStart, PSTR *ppszRet, int *pcchRet )
  1262.     {
  1263.     PSTR  pszCur = pszStart;
  1264.     PSTR  pszTokStart;
  1265.  
  1266.     if( !pszStart || NULL == ppszRet || NULL == pcchRet )
  1267.     return FALSE;
  1268.  
  1269.     // Skip leading whitespace
  1270.     while( *pszCur && (*pszCur == ' ' || *pszCur == '\t'))
  1271.     pszCur++;
  1272.  
  1273.     *ppszRet = NULL;
  1274.     *pcchRet = 0;
  1275.  
  1276.     if( *pszCur )
  1277.     {
  1278.     pszTokStart = pszCur;
  1279.  
  1280.     while( *pszCur && *pszCur != ' ' && *pszCur != '\t' )
  1281.         pszCur++;
  1282.  
  1283.     *ppszRet = pszTokStart;
  1284.     *pcchRet = (int)(pszCur - pszTokStart);
  1285.     }
  1286.  
  1287.     if( *pcchRet != 0 )
  1288.     return TRUE;
  1289.     else
  1290.     return FALSE;
  1291.     }
  1292.  
  1293.  
  1294. /* fMatchToken()
  1295.  *
  1296.  *    Attempts to match the first cchLen characters of pszDatum to the
  1297.  * string at pszString.  The comparison is case-insensitive (this function
  1298.  * is designed for command-line switch matching).
  1299.  *
  1300.  * Returns: TRUE if the first cchLen characters are a match, else FALSE.
  1301.  */
  1302. BOOL fMatchToken( PSTR pszString, PSTR pszDatum, int cchLen )
  1303.     {
  1304.     int i;
  1305.  
  1306.     for( i = 0; i < cchLen; i++ )
  1307.     {
  1308.     if( CharLower( (LPTSTR)MAKELONG( pszString[i], 0 ))
  1309.             != CharLower( (LPTSTR)MAKELONG( pszDatum[i], 0 )))
  1310.         return FALSE;
  1311.     }
  1312.     return TRUE;
  1313.     }
  1314.  
  1315.  
  1316. /* ParseCommandLine()
  1317.  *
  1318.  *    Given a command-line string without the module name, this function will
  1319.  * parse the command line and takes action on whatever it finds there.
  1320.  *
  1321.  * Returns: TRUE if successful, or FALSE if there was an error.
  1322.  */
  1323. BOOL ParseCommandLine(LPSTR lpszCmdLine)
  1324.     {
  1325.     PSTR    pszCur,pszToken;
  1326.     PSTR    ppszFiles[MAXCONTROLS];
  1327.     BOOL    fStartPlaying = FALSE, fStartLooping = FALSE;
  1328.     int     cchTokLen = 0, i, nFilesFound;
  1329.  
  1330.     pszCur = lpszCmdLine;
  1331.  
  1332.     // First get all the command line switches
  1333.     while( fGetToken(pszCur, &pszToken, &cchTokLen) &&
  1334.        (pszToken[0] == '/' || pszToken[0] == '-' ))
  1335.     {
  1336.     pszCur = pszToken + cchTokLen;
  1337.     pszToken++;
  1338.  
  1339.     if( fMatchToken( pszToken, "PLAY", 4 ))
  1340.         {
  1341.         fStartPlaying = TRUE;
  1342.         }
  1343.     else if( fMatchToken( pszToken, "LOOP", 4 ))
  1344.         {
  1345.         fStartLooping = TRUE;
  1346.         }
  1347.     else
  1348.         {
  1349.         // We don't recognize this mysterious switch, so eat it and move on
  1350.         }
  1351.     }
  1352.  
  1353.     // Anything left on the command-line will be treated as a filename and
  1354.     // we'll attempt to open it after we've found them all
  1355.     nFilesFound = 0;
  1356.     while( fGetToken(pszCur, &pszToken, &cchTokLen) && nFilesFound < MAXCONTROLS )
  1357.     {
  1358.     pszCur = pszToken + cchTokLen;
  1359.     ppszFiles[nFilesFound] = new char[cchTokLen+1];
  1360.     // Copy the token out of the command-line string and into our buffer
  1361.     CopyMemory( ppszFiles[nFilesFound], pszToken, cchTokLen*sizeof(char));
  1362.     // Append a NULL terminator to what we just copied (to be safe)
  1363.     *(ppszFiles[nFilesFound] + cchTokLen) = 0;
  1364.     nFilesFound++;
  1365.     }
  1366.     // This function will take the array of strings we've created and open
  1367.     // each string as a file.  It will obey the global fStartPlaying and
  1368.     // fStartLooping flags we may have already set above
  1369.     if( nFilesFound )
  1370.     AppWnd.BatchOpenFiles( ppszFiles, nFilesFound, fStartPlaying, fStartLooping );
  1371.  
  1372.     // Free the space we allocated
  1373.     for( i = 0; i < nFilesFound; i++ )
  1374.     {
  1375.     delete[] ppszFiles[i];
  1376.     ppszFiles[i] = NULL;
  1377.     }
  1378.  
  1379.     // Returning TRUE means the caller should continue doing what they
  1380.     // were doing: we succeeded.
  1381.     return TRUE;
  1382.     }
  1383.  
  1384.  
  1385.