home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / dsshow / shell.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  77.4 KB  |  3,130 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:       shell.c
  6.  *  Content:    Direct Sound show-off.
  7.  *  This app basically uses the direct sound api's and pops up some
  8.  *  controls that the user can play with at runtime to change
  9.  *  the sound frequency, panning, volume, etc.   It has a few
  10.  *  other functions built in.
  11.  *
  12.  *  This app also takes a couple command-line parameters.  The format is:
  13.  *
  14.  *    DSShow [/PLAY [/LOOP]] [file] [file] ...
  15.  *
  16.  *    Specifying /PLAY causes any specified files to be played as they're
  17.  * opened.  Adding the /LOOP causes them to loop as well.  /LOOP without
  18.  * /PLAY means nothing.  Everything else is assumed to be one or more file
  19.  * names.  Filenames can be enclosed in quotes.  This also means you can
  20.  * drag and drop files onto the program's icon
  21.  *
  22.  *
  23.  ***************************************************************************/
  24.  
  25. #define INITGUID
  26. #include <windows.h>
  27. #include <windowsx.h>
  28. #include <commctrl.h>
  29. #include <commdlg.h>
  30. #include <stdio.h>
  31.  
  32. #include <mmsystem.h>
  33. #include <mmreg.h>
  34. #include <msacm.h>
  35. #include <dsound.h>
  36.  
  37.  
  38. #include "wassert.h"
  39. #include "wave.h"
  40.  
  41. #include "resource.h"
  42. #include "shell.h"
  43. #include "dsenum.h"
  44.  
  45.  
  46. // =======================================================================
  47. /* Procedure called when the application is loaded for the first time */
  48. // =======================================================================
  49. BOOL ClassInit( hInstance )
  50. HANDLE hInstance;
  51. {
  52.     WNDCLASS    myClass;
  53.         
  54.     myClass.hCursor             = LoadCursor( NULL, IDC_ARROW );
  55.     myClass.hIcon               = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_ICON3));
  56.     myClass.lpszMenuName        = MAKEINTRESOURCE(IDR_MAINMENU);
  57.     myClass.lpszClassName       = (LPSTR)szAppName;
  58.     myClass.hbrBackground       = (HBRUSH)(COLOR_WINDOW);
  59.     myClass.hInstance           = hInstance;
  60.     myClass.style               = CS_HREDRAW | CS_VREDRAW;
  61.     myClass.lpfnWndProc         = WndProc;
  62.     myClass.cbClsExtra          = 0;
  63.     myClass.cbWndExtra          = 0;
  64.  
  65.     if (!RegisterClass( &myClass ) )
  66.        return FALSE;
  67.  
  68.     return TRUE;        /* Initialization succeeded */
  69. }
  70.  
  71.  
  72. // =======================================================================
  73. /* This "hook procedure" is called by the common dialog code for certain
  74.  *   events that may occur during the life of our nested dialog structure.
  75.  *     We nest the Explorer style dialog inside our file open dialog so we
  76.  *   can addd a check box for stick buffers.
  77.  */
  78.  // =======================================================================
  79. UINT CALLBACK FileOpenCustomTemplateDlgProc( hDlg, message, wParam, lParam )
  80. HWND hDlg;
  81. UINT message;
  82. WPARAM wParam;
  83. LPARAM lParam;
  84. {
  85.     static LPOPENFILENAME   lpofn = NULL;
  86.  
  87.     switch( message )
  88.     {
  89.     case WM_INITDIALOG:
  90.         lpofn = (LPOPENFILENAME)lParam;
  91.         
  92.         /* Set the flag to match the current state of the check box control */
  93.         *((LPBOOL)lpofn->lCustData) = SendDlgItemMessage( hDlg, IDC_FONEST_STICKY,
  94.                                                             BM_GETCHECK, 0, 0 );
  95.         return TRUE;
  96.  
  97.     case WM_NOTIFY:
  98.         switch(((LPOFNOTIFY)lParam)->hdr.code)
  99.         {
  100.         case CDN_SELCHANGE:
  101.             /* Use this area to process anything that must be updated when the
  102.              * user changes the selection in the Common Dialog Box.
  103.              *   NOTE: Provided only for informational purposes
  104.              */
  105.             return FALSE;
  106.  
  107.         case CDN_FILEOK:
  108.             /* We can do lots of things in this notification message.  The most
  109.              * important is that we can decide whether the Common Dialog call will
  110.              * go through or whether it will fail.  I decided to handle the checkbox
  111.              * control in this one place versus 4 others... -PRN
  112.              */
  113.             Assert( lpofn != NULL );
  114.             *((LPBOOL)lpofn->lCustData) = SendDlgItemMessage( hDlg, IDC_FONEST_STICKY,
  115.                                                                 BM_GETCHECK, 0, 0 );
  116.             /* Returning zero signifies that we "approve" of the OK command,
  117.              * and allows the common dialog to finish.
  118.              */
  119.             return FALSE;
  120.         }
  121.         /* Let the default dialog do/continue processing */
  122.         return FALSE;
  123.     }
  124.     return FALSE;
  125. }
  126.  
  127.  
  128. // =======================================================================
  129. // =======================================================================
  130. int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
  131. HINSTANCE hInstance, hPrevInstance;
  132. LPSTR lpszCmdLine;
  133. int cmdShow;
  134. {
  135.     MSG   msg;
  136.     HWND  hWnd;
  137.  
  138.     // We must call this to ensure the common controls are setup for
  139.     // this application
  140.     InitCommonControls();
  141.  
  142.     if (!hPrevInstance) {
  143.     /* Call initialization procedure if this is the first instance */
  144.     if (!ClassInit( hInstance ))
  145.         return FALSE;
  146.     }
  147.  
  148.     
  149.     hWnd = CreateWindow((LPSTR)szAppName,
  150.                         (LPSTR)szMessage,
  151.                         WS_OVERLAPPEDWINDOW,
  152.                         CW_USEDEFAULT,    
  153.                         CW_USEDEFAULT,    
  154.                         DX_MINWINDOW,     
  155.                         DY_MINWINDOW,     
  156.                         (HWND)NULL,        
  157.                         (HMENU)NULL,      
  158.                         (HANDLE)hInstance, 
  159.                         (LPSTR)NULL        
  160.                         );
  161.  
  162.     if (!hWnd) return (int)msg.wParam;
  163.  
  164.     // Make a long line across the top.
  165.     CreateWindow(
  166.     "STATIC", 
  167.     "", 
  168.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  169.     0,
  170.     0,
  171.     8000, 
  172.     2,              
  173.     hWnd, 
  174.     (HMENU)0, 
  175.     hInst, 
  176.     NULL);
  177.     
  178.  
  179.     /* Save instance handle for DialogBox */
  180.     hInst = hInstance;
  181.     
  182.     ShowWindow( hWnd, cmdShow );
  183.  
  184.     if( lpszCmdLine && *lpszCmdLine )
  185.         if( !ParseCommandLine( lpszCmdLine ))
  186.         goto Exit_WinMain;
  187.     
  188.     /* Polling messages from event queue */
  189.     while (GetMessage((LPMSG)&msg, NULL, 0, 0)) {
  190.     TranslateMessage((LPMSG)&msg);
  191.     DispatchMessage((LPMSG)&msg);
  192.     }
  193.  
  194. Exit_WinMain:
  195.     DestroyWindow(hWnd);
  196.     UnregisterClass(szAppName, hInstance);
  197.     return (int)msg.wParam;
  198. }
  199.  
  200. // =======================================================================
  201. /*  This function updates the status window by writing the specified
  202.     string to the window, prepended by a string indicating whether
  203.     the buffer is in hardware or software
  204. */
  205. // =======================================================================
  206. void UpdateStatus(FILEINFO *pFileInfo, DWORD dwStatus)
  207. {
  208.     TCHAR szStatus[200];
  209.     DWORD dwPlay, dwWrite;
  210.     HRESULT hr;
  211.  
  212.     lstrcpy(szStatus, pFileInfo->fHardware ? szHW : szSW);
  213.     if (dwStatus & DSBSTATUS_BUFFERLOST)
  214.     {
  215.     lstrcat(szStatus, szLost);
  216.     SendMessage(pFileInfo->hWndStatus_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  217.     }
  218.     else if (dwStatus & DSBSTATUS_PLAYING)
  219.     {
  220.     lstrcat(szStatus, szPlaying);
  221.     SendMessage(pFileInfo->hWndStatus_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  222.     }
  223.     else
  224.     {
  225.     lstrcat(szStatus, szStopped);
  226.     SendMessage(pFileInfo->hWndStatus_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  227.     }
  228.  
  229.     if (pFileInfo->fSticky)
  230.     {
  231.     lstrcat(szStatus, szSticky);
  232.     SendMessage(pFileInfo->hWndStatus_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  233.     }
  234.     hr = IDirectSoundBuffer_GetCurrentPosition(pFileInfo->pDSB, &dwPlay, &dwWrite);
  235.     if (DS_OK == hr) {
  236.     wsprintf(szStatus, szFmtPlayPosition, dwPlay);
  237.     SendMessage(pFileInfo->hWndPlayPosition_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  238.     wsprintf(szStatus, szFmtWritePosition, dwWrite);
  239.     SendMessage(pFileInfo->hWndWritePosition_TXT, WM_SETTEXT, 0, (LPARAM)szStatus);
  240.     }
  241.  
  242.     return;
  243. }
  244.  
  245. // =======================================================================
  246. /*  This function updates the main window title to show some
  247.     relevant information about the direct sound object
  248. */
  249. // =======================================================================
  250. void UpdateMainStatus()
  251. {
  252.     DSCAPS  dsc;
  253.     TCHAR   szTitle[200];
  254.     
  255.     // Update main window title with some relevant info
  256.     dsc.dwSize = sizeof(dsc);
  257.     IDirectSound_GetCaps(gpds, &dsc);
  258.     wsprintf(szTitle, "%s : free hw memory = %dKb, free hw buffers = %d",
  259.      szMessage, (dsc.dwFreeHwMemBytes+512)/1024,
  260.      dsc.dwFreeHwMixingAllBuffers);
  261.     SendMessage(hWndMain, WM_SETTEXT, 0, (LPARAM)szTitle);
  262.     return;
  263. }
  264.  
  265.  
  266. // =======================================================================
  267. /*  This routine will set up everything needed for the app to run.
  268.  
  269.     Input:
  270.     hWnd                - App main window handle
  271.  
  272.     Output:
  273.     None.
  274.  
  275. */
  276. // =======================================================================
  277. int AppInit(HWND hWnd)
  278. {
  279.     UINT            cT;
  280.     DSBUFFERDESC    dsbd;
  281.     BOOL        fUseGuid;
  282.     HRESULT         hr;
  283.     DWORD           dw;
  284.  
  285.     // Set up the global window handle.
  286.     hWndMain = hWnd;
  287.  
  288.     // Set up the global File...Open dialog's start directory
  289.     GetMediaStartPath();
  290.  
  291.     // Set up the file info header
  292.     FileInfoFirst.pNext = NULL;
  293.     FileInfoFirst.pwfx = NULL;
  294.     FileInfoFirst.cox = COX_STARTCONTROL;
  295.     FileInfoFirst.coy = COY_STARTCONTROL;
  296.  
  297.     // Clear the coordinate buffer.  Used to find the next available
  298.     // position to use for a new control.  -1 is the invalid value.
  299.     for (cT=0; cT<MAXCONTROLS; cT++) rgfcoxAvail[cT] = FALSE;
  300.  
  301.     // Setup the timer...
  302.     if ((dwTimer = SetTimer(hWnd, 1, TIMERPERIOD, NULL)) == 0) 
  303.     {
  304.         MessageBox(hWnd, "Cannot allocate timer, aborting", "DirectSound Demo", MB_OK|MB_ICONSTOP);
  305.         return -1;
  306.     }
  307.  
  308.     // Now set up all the direct sound stuff...
  309.  
  310.     // Get the largest waveformatex structure.
  311.     if (MMSYSERR_NOERROR != acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &dw))
  312.     {
  313.     MessageBox(hWnd, "ACM Metrics failed, aborting", "DirectSound Demo",
  314.            MB_OK|MB_ICONSTOP);
  315.     return -1;
  316.     }
  317.  
  318.  
  319.     // Setup the format, frequency, volume, etc.
  320.     if ((FileInfoFirst.pwfx = GlobalAllocPtr(GPTR, dw)) == NULL)
  321.     {
  322.     MessageBox(hWnd, "Out of Memory", "DirectSound Demo",
  323.            MB_OK|MB_ICONSTOP);
  324.     return -1;
  325.     }
  326.  
  327.  
  328.  
  329.     FileInfoFirst.pwfx->wFormatTag = WAVE_FORMAT_PCM;
  330.     FileInfoFirst.pwfx->nChannels = 2;
  331.     FileInfoFirst.pwfx->nSamplesPerSec = 22050;
  332.     FileInfoFirst.pwfx->nAvgBytesPerSec = 22050*2*2;
  333.     FileInfoFirst.pwfx->nBlockAlign = 4;
  334.     FileInfoFirst.pwfx->wBitsPerSample = 16;
  335.     FileInfoFirst.pwfx->cbSize = 0;
  336.     
  337. #ifdef STARTEIGHTBITS
  338.  
  339.     FileInfoFirst.pwfx->wFormatTag = WAVE_FORMAT_PCM;
  340.     FileInfoFirst.pwfx->nChannels = 2;
  341.     FileInfoFirst.pwfx->nSamplesPerSec = 22050;
  342.     FileInfoFirst.pwfx->nAvgBytesPerSec = 22050*1*2;
  343.     FileInfoFirst.pwfx->nBlockAlign = 2;
  344.     FileInfoFirst.pwfx->wBitsPerSample = 8;
  345.     FileInfoFirst.pwfx->cbSize = 0;
  346. #endif
  347.  
  348. #ifdef STARTMONO    
  349.     FileInfoFirst.pwfx->wFormatTag = WAVE_FORMAT_PCM;
  350.     FileInfoFirst.pwfx->nChannels = 1;
  351.     FileInfoFirst.pwfx->nSamplesPerSec = 22050;
  352.     FileInfoFirst.pwfx->nAvgBytesPerSec = 22050*1*2;
  353.     FileInfoFirst.pwfx->nBlockAlign = 2;
  354.     FileInfoFirst.pwfx->wBitsPerSample = 16;
  355.     FileInfoFirst.pwfx->cbSize = 0;
  356. #endif
  357.     
  358.     // Optionally enumerate DSOUND devices and allow the user to pick one...
  359.  
  360.     if (!SUCCEEDED(CoInitialize(NULL))) {
  361.     MessageBox(hWnd, "Failed to initialize COM library", "DirectSound Demo", MB_OK | MB_ICONSTOP);
  362.     return -1;
  363.     }
  364.     
  365.     fEnumDrivers = (BOOL)GetProfileInt( "DSSHOW", "EnumDrivers", FALSE );
  366.     fUseGuid = (fEnumDrivers && !DoDSoundEnumerate(&guID));
  367.  
  368.     // Create the direct sound object.
  369.     hr = CoCreateInstance(&CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
  370.               &IID_IDirectSound, &gpds);
  371.  
  372.     if (SUCCEEDED(hr) && (NULL != gpds))
  373.     {
  374.         hr = IDirectSound_Initialize(gpds, fUseGuid ? &guID : NULL);
  375.         if (SUCCEEDED(hr)) 
  376.         {
  377.             // Note we need to set the level to be priority to set the
  378.             // format of the primary buffer
  379.             hr = IDirectSound_SetCooperativeLevel(gpds, hWndMain, DSSCL_PRIORITY);
  380.             if (SUCCEEDED(hr)) 
  381.             {
  382.  
  383.             // Set up the primary direct sound buffer.
  384.             ZeroMemory(&dsbd, sizeof(dsbd));
  385.             dsbd.dwSize = sizeof(dsbd);
  386.             dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  387.     
  388.             hr = IDirectSound_CreateSoundBuffer(gpds, &dsbd, &(FileInfoFirst.pDSB), NULL);
  389.             if (SUCCEEDED(hr)) 
  390.             {
  391.                 hr = IDirectSoundBuffer_Play(FileInfoFirst.pDSB, 0, 0, DSBPLAY_LOOPING);
  392.                 if (SUCCEEDED(hr)) 
  393.                 {
  394.                     UpdateMainStatus();
  395.                 } 
  396.                 else 
  397.                 {
  398.                     MessageBox(hWnd, "Cannot play primary buffer","DirectSound Demo", MB_OK|MB_ICONSTOP);
  399.                     IDirectSoundBuffer_Release(FileInfoFirst.pDSB);
  400.                     FileInfoFirst.pDSB = NULL;
  401.                 }        
  402.             } 
  403.             else 
  404.             {
  405.                 MessageBox(hWnd, "Cannot create primary buffer","DirectSound Demo", MB_OK|MB_ICONSTOP);
  406.             }
  407.         } 
  408.         else 
  409.         {
  410.         MessageBox(hWnd, "DirectSound SetCooperativeLevel failed","DirectSound Demo", MB_OK|MB_ICONSTOP);
  411.         }
  412.     } //initialize
  413.     else 
  414.     {
  415.         MessageBox(hWnd, "Failed to Initialize DirectSound object", "DirectSound Demo", MB_OK | MB_ICONSTOP);
  416.     }
  417.  
  418.     if (!SUCCEEDED(hr)) 
  419.     {
  420.         IDirectSound_Release(gpds);
  421.         gpds = NULL;
  422.     }
  423.  
  424.  } 
  425. else 
  426. {
  427.     MessageBox(hWnd, "Failed to create DirectSound COM object",
  428.            "DirectSound Demo", MB_OK | MB_ICONSTOP);
  429. }
  430.  
  431. if (SUCCEEDED(hr)) 
  432. {
  433.     return 0;
  434. else 
  435. {
  436.     CoUninitialize();
  437.     return -1;
  438. }
  439. }
  440.  
  441. // =======================================================================
  442. /*  This will destroy all the created objects, allocated memory, etc.  Must be called
  443.     before termination of app.
  444.  
  445.     Input:
  446.     hWnd                - Window handle of main window
  447.  
  448.     Output:
  449.     None.
  450.  
  451. */
  452. // =======================================================================
  453. void AppDestroy( HWND hWnd )
  454. {
  455.  
  456.     HRESULT     hr = 0;
  457.  
  458.     if (dwTimer != 0)
  459.     {
  460.     KillTimer(hWnd, dwTimer);
  461.     dwTimer = 0;
  462.     }
  463.  
  464.  
  465.     StopAllDSounds(hWnd, &FileInfoFirst);
  466.     FreeAllList(hWnd, &FileInfoFirst);
  467.  
  468.  
  469.     // Destroy the direct sound buffer.
  470.     if(FileInfoFirst.pDSB != NULL) 
  471.     {
  472.     IDirectSoundBuffer_Stop(FileInfoFirst.pDSB);
  473.     IDirectSoundBuffer_Release(FileInfoFirst.pDSB);
  474.     FileInfoFirst.pDSB = NULL;
  475.     }
  476.  
  477.     // Destroy the direct sound object.
  478.     if (gpds != NULL)
  479.     {
  480.     IDirectSound_Release(gpds);
  481.     gpds = NULL;
  482.     CoUninitialize();
  483.     }
  484.  
  485.     if (FileInfoFirst.pwfx != NULL)
  486.     {
  487.     GlobalFreePtr(FileInfoFirst.pwfx);
  488.     FileInfoFirst.pwfx = NULL;
  489.     }
  490.  
  491.     if (FileInfoFirst.pbData != NULL)
  492.     {
  493.     GlobalFreePtr(FileInfoFirst.pbData);
  494.     FileInfoFirst.pbData = NULL;
  495.     }
  496.  
  497.     WriteProfileString( "DSSHOW", "EnumDrivers", fEnumDrivers ? "1" : "0" );
  498.  
  499. }
  500.  
  501. // =======================================================================
  502. /* Procedures which make up the window class. */
  503. // =======================================================================
  504. long FAR PASCAL WndProc( hWnd, message, wParam, lParam )
  505. HWND hWnd;
  506. unsigned message;
  507. WPARAM wParam;
  508. LPARAM lParam;
  509. {
  510.  
  511.  
  512.  
  513.     switch (message)
  514.     {
  515.  
  516.     case WM_CREATE:
  517.         if (AppInit(hWnd)) return (-1);
  518.         break;
  519.  
  520.     case WM_TIMER:  
  521.         if (!UIMainWindowTimerHandler(hWnd, wParam, lParam))
  522.         return(DefWindowProc(hWnd, message, wParam, lParam));                           
  523.         break;
  524.         
  525.     
  526.     case WM_HSCROLL:
  527.         if (!UIMainWindowHSBHandler(hWnd, wParam, lParam))
  528.         return(DefWindowProc(hWnd, message, wParam, lParam));
  529.             
  530.         break;
  531.  
  532.     case WM_VSCROLL:
  533.         if (!UIMainWindowVSBHandler(hWnd, wParam, lParam))
  534.         return(DefWindowProc(hWnd, message, wParam, lParam));
  535.         break;
  536.             
  537.  
  538.         case WM_INITMENU:
  539.             if((HMENU)wParam != GetMenu( hWnd ))
  540.                 break;
  541.             CheckMenuItem((HMENU)wParam, IDPD_ENUMDRIVERS,
  542.                 fEnumDrivers ? MF_CHECKED : MF_UNCHECKED );
  543.             break;
  544.  
  545.         case WM_COMMAND:
  546.         if (!UIMainWindowCMDHandler(hWnd, wParam, lParam))
  547.         return(DefWindowProc(hWnd, message, wParam, lParam));
  548.         break;
  549.         
  550.         break;
  551.     
  552.     /*case WM_PAINT:
  553.         {           
  554.         
  555.         break;
  556.         }*/
  557.  
  558.  
  559.     case WM_DESTROY:
  560.         AppDestroy(hWnd);
  561.         PostQuitMessage( 0 );
  562.         break;
  563.     
  564.     default:
  565.         return DefWindowProc( hWnd, message, wParam, lParam );
  566.         break;
  567.         
  568.     }
  569.     
  570.     return(0L);
  571. }
  572.  
  573. // =======================================================================
  574. /*  This routine will pop up the open file dialog and open a file, and make any internal
  575.     arrangements so we know the file is loaded.
  576.  
  577.     Input:
  578.     hWnd            -   Handle of parent window.
  579.  
  580.     Output:
  581.     None.
  582.  
  583. */
  584. // =======================================================================
  585. void PD_FileOpen( HWND hWnd )
  586. {
  587.  
  588.     char            szFileName[MAX_PATH];
  589.     UINT            cSamples;
  590.     FILEINFO        *pFileInfo                  = NULL;
  591.     int             nFileName;
  592.     BOOL            fSticky;
  593.  
  594.     if (GetNumControls(&FileInfoFirst) >= MAXCONTROLS)
  595.     {
  596.     MessageBox(hWnd, "No more controls allowed",
  597.        "Hold on a sec...", MB_OK);
  598.     return;
  599.     }
  600.  
  601.     // Open the file, and check its format, etc.
  602.     if (OpenFileDialog(hWnd, szFileName, &nFileName, &fSticky))
  603.     {
  604.  
  605.     // Allocate the memory for the structure.
  606.     if ((pFileInfo = GlobalAllocPtr(GPTR, sizeof(FILEINFO))) == NULL)
  607.     {
  608.     MessageBox(hWnd, "Cannot add this file",
  609.            "Out of Memory", MB_OK|MB_ICONSTOP);
  610.     goto ERROR_DONE_ROUTINE;
  611.     }
  612.  
  613.     pFileInfo->pbData   = NULL;
  614.     pFileInfo->pwfx     = NULL;
  615.     pFileInfo->pDSB     = NULL;
  616.     pFileInfo->fSticky  = fSticky;
  617.     strcpy(pFileInfo->szFileName, szFileName);
  618.     
  619.     if (WaveLoadFile(szFileName, &pFileInfo->cbSize, 
  620.         &cSamples, &pFileInfo->pwfx, &pFileInfo->pbData) != 0)
  621.     {
  622.     MessageBox(hWnd, "Bad wave file or file too big to fit in memory",
  623.         "Cannot load wave", MB_OK|MB_ICONSTOP);
  624.     goto ERROR_DONE_ROUTINE;
  625.     }
  626.  
  627.     GetNextControlCoords(&FileInfoFirst,
  628.              &pFileInfo->cox, &pFileInfo->coy);
  629.  
  630.     if (NewDirectSoundBuffer(pFileInfo) != 0)
  631.     {
  632.     MessageBox(hWnd, "Cannot create new buffer",
  633.            "Direct Sound Error", MB_OK|MB_ICONSTOP);
  634.     goto ERROR_DONE_ROUTINE;
  635.     }
  636.     
  637.     Assert(pFileInfo->pbData != NULL);
  638.  
  639.     // If we fail after this, make sure to update the list!!!
  640.     if (AddToList(&FileInfoFirst, pFileInfo) != 0)
  641.     {
  642.     MessageBox(hWnd, "Cannot add file to list",
  643.            "Out of Memory", MB_OK|MB_ICONSTOP);
  644.     goto ERROR_DONE_ROUTINE;
  645.     }
  646.  
  647.     pFileInfo->nFileName = nFileName;
  648.     CreateControl(hWnd, pFileInfo, pFileInfo->pwfx->nSamplesPerSec,
  649.           (MAXPAN_TB-MINPAN_TB)/2, MINVOL_TB );
  650.     ChangeOutputVol(pFileInfo);
  651.     ChangeOutputFreq(pFileInfo);
  652.     ChangeOutputPan(pFileInfo);
  653.     UpdateMainStatus();
  654.  
  655.     }
  656.  
  657.     goto DONE_ROUTINE;
  658.        
  659. ERROR_DONE_ROUTINE:
  660.     if (pFileInfo != NULL)
  661.     {
  662.     
  663.     ReleaseDirectSoundBuffer(pFileInfo);
  664.  
  665.     if (pFileInfo->pwfx != NULL)
  666.     {
  667.     GlobalFreePtr(pFileInfo->pwfx);
  668.         
  669.     }
  670.     if (pFileInfo->pbData != NULL)
  671.     {
  672.     GlobalFreePtr(pFileInfo->pbData);           
  673.     }
  674.  
  675.     GlobalFreePtr(pFileInfo);
  676.     pFileInfo = NULL;
  677.     }
  678.  
  679. DONE_ROUTINE:
  680.     return;
  681.  
  682. }
  683.  
  684. // =======================================================================
  685. /*  This routine will initialize a new direct sound buffer,
  686.     set the data in the buffer, 
  687.     set the rate, format, etc...
  688.  
  689.     Input:
  690.     pFileInfo   -   Pointer to file info with all
  691.     nessecary info filled, 
  692.     like pbData, cbData, etc...
  693.  
  694.     Output:
  695.     0 if successful, else the error code.
  696.  
  697. */
  698. // =======================================================================
  699. int NewDirectSoundBuffer(FILEINFO *pFileInfo)
  700. {
  701.  
  702.     DSBUFFERDESC        dsbd;
  703.     DSBCAPS         dsbc;
  704.     HRESULT         hr;
  705.     BYTE            *pbData         = NULL;
  706.     BYTE            *pbData2        = NULL;
  707.     DWORD           dwLength;
  708.     DWORD           dwLength2;
  709.  
  710.     // Set up the direct sound buffer. 
  711.     memset(&dsbd, 0, sizeof(DSBUFFERDESC));
  712.     dsbd.dwSize                 = sizeof(DSBUFFERDESC);
  713.     dsbd.dwFlags                = 0;
  714.     dsbd.dwFlags                |= DSBCAPS_STATIC;
  715.     // Use new GetCurrentPosition() accuracy (DirectX 2 feature)
  716.     dsbd.dwFlags                |= DSBCAPS_CTRLDEFAULT | DSBCAPS_GETCURRENTPOSITION2;
  717.     if (pFileInfo->fSticky)
  718.         dsbd.dwFlags |= DSBCAPS_STICKYFOCUS;
  719.     dsbd.dwBufferBytes               = pFileInfo->cbSize;
  720.     dsbd.lpwfxFormat            = pFileInfo->pwfx;
  721.     if ((hr = gpds->lpVtbl->CreateSoundBuffer(gpds,
  722.               &dsbd,
  723.               &(pFileInfo->pDSB),
  724.               NULL )) != 0)
  725.     {
  726.     goto ERROR_IN_ROUTINE;
  727.     }
  728.  
  729.     // Ok, lock the sucker down, and copy the memory to it.
  730.     if ((hr = pFileInfo->pDSB->lpVtbl->Lock(pFileInfo->pDSB,
  731.             0,
  732.             pFileInfo->cbSize,
  733.             &pbData,
  734.             &dwLength,
  735.             &pbData2,
  736.             &dwLength2,
  737.                         0L)) != 0)
  738.     {
  739.     goto ERROR_IN_ROUTINE;
  740.     }
  741.  
  742.     Assert(pbData != NULL);
  743.     memcpy(pbData, pFileInfo->pbData, pFileInfo->cbSize);
  744.  
  745.     // Ok, now unlock the buffer, we don't need it anymore.
  746.     if ((hr = pFileInfo->pDSB->lpVtbl->Unlock(pFileInfo->pDSB,
  747.                           pbData, pFileInfo->cbSize,
  748.                           NULL, 0)) != 0)
  749.     {
  750.     goto ERROR_IN_ROUTINE;
  751.     }
  752.  
  753.     pbData = NULL;
  754.  
  755.     if ((hr = pFileInfo->pDSB->lpVtbl->SetVolume(pFileInfo->pDSB,
  756.         MAXVOL_VAL)) != 0)
  757.     {
  758.     goto ERROR_IN_ROUTINE;
  759.     }
  760.  
  761.     if ((hr = pFileInfo->pDSB->lpVtbl->SetPan(pFileInfo->pDSB,
  762.         MIDPAN_VAL)) != 0)
  763.     {
  764.     goto ERROR_IN_ROUTINE;
  765.     }
  766.  
  767.     dsbc.dwSize = sizeof(dsbc);
  768.     if (hr = IDirectSoundBuffer_GetCaps(pFileInfo->pDSB, &dsbc))
  769.     {
  770.     goto ERROR_IN_ROUTINE;
  771.     }
  772.  
  773.     if (dsbc.dwFlags & DSBCAPS_LOCHARDWARE) {
  774.     pFileInfo->fHardware = TRUE;
  775.     } else {
  776.     pFileInfo->fHardware = FALSE;
  777.     }
  778.  
  779.     goto DONE_ROUTINE;
  780.  
  781. ERROR_IN_ROUTINE:
  782.     if (pbData != NULL)
  783.     {
  784.     hr = pFileInfo->pDSB->lpVtbl->Unlock(pFileInfo->pDSB, pbData,
  785.                         pFileInfo->cbSize, NULL, 0);
  786.     pbData = NULL;
  787.     }
  788.  
  789.     if (pFileInfo->pDSB != NULL)
  790.     {
  791.     pFileInfo->pDSB->lpVtbl->Release(pFileInfo->pDSB);
  792.     pFileInfo->pDSB = NULL;
  793.     }
  794.     
  795. DONE_ROUTINE:
  796.  
  797.     return(hr); 
  798.  
  799. }
  800.  
  801. // =======================================================================
  802. /*  This routine will release a direct sound buffer,
  803.     freeing up memory, resources, 
  804.     whatever.
  805.  
  806.     Input:
  807.     pFileInfo   -   Pointer to the file info,
  808.         with the proper stuff set.
  809.  
  810.     Output: 
  811.     0 if successful, else the error code.
  812.  
  813. */
  814. // =======================================================================
  815. int ReleaseDirectSoundBuffer( FILEINFO *pFileInfo )
  816. {
  817.  
  818.     if (pFileInfo->pDSB != NULL)
  819.     {
  820.     pFileInfo->pDSB->lpVtbl->Release(pFileInfo->pDSB);
  821.     pFileInfo->pDSB = NULL; 
  822.     }
  823.  
  824.     return(0);
  825.  
  826. }
  827.  
  828. // =======================================================================
  829. /*  This routine will find the next x and y coordinates to
  830.     write the control to.
  831.     The rgfcoxAvail is an array of booleans.
  832.     If false, then the index can be 
  833.     used as an x coordinate.
  834.  
  835.     Input:
  836.     pFileInfoHead - Header of the linked list.
  837.     pcox, pcoy    - Filled upon return with next
  838.         coordinates to use.
  839.     
  840.     Output:
  841.     Only pcox and pcoy change.
  842.     
  843. */
  844. // =======================================================================    
  845. void GetNextControlCoords(                     
  846.             FILEINFO    *pFileInfoHead, 
  847.             int         *pcox, 
  848.             int         *pcoy
  849.             )
  850. {
  851.     UINT            cT;
  852.  
  853.     for (cT=0; cT<MAXCONTROLS; cT++)
  854.     {
  855.     if (rgfcoxAvail[cT] == FALSE)
  856.     {
  857.     rgfcoxAvail[cT] = TRUE;
  858.     break;
  859.     }
  860.         
  861.     }
  862.  
  863.     if (cT == MAXCONTROLS)
  864.     {
  865.     Assert(FALSE);
  866.     // Couldn't find a place to put control, shouldn't happen though.
  867.     cT = 666;       // Well, at least put it off screen.
  868.     }
  869.  
  870.     *pcox = cT*DX_CONTROLSPACING+COX_STARTCONTROL;      //Offsetting the text from the border
  871.     *pcoy = COY_STARTCONTROL;
  872.     
  873.  
  874. }
  875.  
  876. // =======================================================================
  877. /*  CreateControl
  878.  
  879.     This will create the control used for the window, actually it is a
  880.     bundle of controls put together.  I was thinking of a good way to
  881.     figure out id codes for the controls but found no good way except a
  882.     "funny" way...I'm going to use the x coordinate of the control as the
  883.     id for the first control, then id+1 for the second control.  Since
  884.     all the controls have different x coordinates, this is fine, as long
  885.     as the # of windows in the control is not more than the spacing of
  886.     the controls.
  887.  
  888.     Input:
  889.     hWnd                -   Parent Window.
  890.     pFileInfo           -   Pointer to FileInfo structure with the cox and coy filled.
  891.     dwFreq, dwPan, dwVol-   Default track bar values.
  892.  
  893.     Output:
  894.     0 if successful, else the error code.
  895.  
  896. */
  897. // =======================================================================
  898. int CreateControl(HWND hWnd, FILEINFO *pFileInfo, DWORD dwFreq,DWORD dwPan, DWORD dwVol)
  899. {
  900.     int        cox, coy;
  901.     int     coxOld, coyOld;
  902.     int     nError = 0;
  903.     DWORD    idBase;
  904.     SIZE    Size;       
  905.     HDC     hDC = NULL;
  906.     DWORD dwMinFreq, dwMaxFreq;
  907.  
  908.     /* Figure out the values of dwPan and dwVol that the track bars like */
  909.  
  910.     idBase = pFileInfo->cox;
  911.     Assert(pFileInfo != NULL);
  912.     cox = pFileInfo->cox+DX_TEXTSPACING;
  913.     coy = pFileInfo->coy+DY_TEXTSPACING;        //We may have to shift this
  914.  
  915.     coxOld = cox;
  916.     coyOld = coy;
  917.     coy -= 8;                       //We must adjust to fit the text in the border
  918.  
  919.     if ((hDC = GetDC(hWnd)) == NULL)
  920.     {
  921.     nError = -1;
  922.     goto DONE_ROUTINE;
  923.     }
  924.  
  925.  
  926.     if (!GetTextExtentPoint32(hDC, pFileInfo->szFileName+pFileInfo->nFileName, strlen(pFileInfo->szFileName+pFileInfo->nFileName), &Size))
  927.     {
  928.     nError = -1;
  929.     goto DONE_ROUTINE;
  930.     }
  931.  
  932.     //Creates the Filename window
  933.     if ((pFileInfo->hWndFileName_TXT = CreateWindow(
  934.     "STATIC", 
  935.     pFileInfo->szFileName+pFileInfo->nFileName, 
  936.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  937.     cox,
  938.     coy,                                                                    
  939.     DX_FILENAME_TXT,                                                                        
  940.     Size.cy,              
  941.     hWnd, 
  942.     (HMENU)0, 
  943.     hInst, 
  944.     NULL)) == NULL)
  945.     {
  946.     nError = -1;
  947.     goto DONE_ROUTINE;
  948.     }   
  949.                                                                         //Create line under Filename            
  950.     cox += DX_LOOPEDSPACING;
  951.     coy += Size.cy + DY_TEXTSPACING + DY_LOOPEDSPACING;
  952.  
  953.     if ((pFileInfo->hWndFileName_EDGE = CreateWindow(
  954.     "STATIC", 
  955.     "", 
  956.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  957.     cox,
  958.     coy - (DY_LOOPEDSPACING+DY_TEXTSPACING)/2,
  959.     DX_LINEEDGE, 
  960.     DY_LINEEDGE,                
  961.     hWnd, 
  962.     (HMENU)0, 
  963.     hInst, 
  964.     NULL)) == NULL)
  965.     {
  966.     nError = -1;
  967.     goto DONE_ROUTINE;
  968.     }   
  969.  
  970.     // Now create status if required.
  971.     
  972.     #ifdef SHOWSTATUS   
  973.  
  974.     if (!GetTextExtentPoint32(hDC, szPlaying, strlen(szPlaying), &Size))
  975.     {
  976.     nError = -1;
  977.     goto DONE_ROUTINE;
  978.     }
  979.  
  980.  
  981.     //Creates Status Window
  982.     if ((pFileInfo->hWndStatus_TXT = CreateWindow(
  983.     "STATIC", 
  984.     "",
  985.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  986.     cox,
  987.     coy,
  988.     DX_STATUS_TXT, 
  989.     Size.cy, // + DY_TEXTSPACING,               
  990.     hWnd, 
  991.     (HMENU)0, 
  992.     hInst, 
  993.     NULL)) == NULL)
  994.     {
  995.     nError = -1;
  996.     goto DONE_ROUTINE;
  997.     }   
  998.  
  999.     cox += DX_LOOPEDSPACING;
  1000.     coy += Size.cy + DY_TEXTSPACING + DY_LOOPEDSPACING;
  1001.  
  1002.     //Create line under Status
  1003.     if ((pFileInfo->hWndStatus_EDGE = CreateWindow(
  1004.     "STATIC", 
  1005.     "", 
  1006.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1007.     cox,
  1008.     coy - (DY_LOOPEDSPACING+DY_TEXTSPACING)/2,
  1009.     DX_LINEEDGE, 
  1010.     DY_LINEEDGE,                
  1011.     hWnd, 
  1012.     (HMENU)0, 
  1013.     hInst, 
  1014.     NULL)) == NULL)
  1015.     {
  1016.     nError = -1;
  1017.     goto DONE_ROUTINE;
  1018.     }
  1019.  
  1020.     //Creates PlayPos Window
  1021.     if ((pFileInfo->hWndPlayPosition_TXT = CreateWindow(
  1022.     "STATIC", "",
  1023.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
  1024.     cox, 
  1025.     coy,
  1026.     DX_STATUS_TXT, 
  1027.     Size.cy,
  1028.     hWnd, 
  1029.     NULL, 
  1030.     hInst, 
  1031.     NULL)) == NULL)
  1032.     {
  1033.     nError = -1;
  1034.     goto DONE_ROUTINE;
  1035.     }
  1036.  
  1037.     cox += DX_LOOPEDSPACING;
  1038.     coy += Size.cy + DY_TEXTSPACING + DY_LOOPEDSPACING; //Create line under PlayPos
  1039.     
  1040.     if ((pFileInfo->hWndPlayPosition_EDGE = CreateWindow(
  1041.     "STATIC", "", 
  1042.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1043.     cox, 
  1044.     coy - (DY_LOOPEDSPACING+DY_TEXTSPACING)/2,
  1045.     DX_LINEEDGE, 
  1046.     DY_LINEEDGE,               
  1047.     hWnd, 
  1048.     NULL, 
  1049.     hInst, 
  1050.     NULL)) == NULL)
  1051.     {
  1052.     nError = -1;
  1053.     goto DONE_ROUTINE;
  1054.     }
  1055.     
  1056.     //Creates WritePos Window
  1057.     if ((pFileInfo->hWndWritePosition_TXT = CreateWindow(
  1058.     "STATIC", "",
  1059.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
  1060.     cox, 
  1061.     coy,
  1062.     DX_STATUS_TXT, 
  1063.     Size.cy,
  1064.     hWnd, 
  1065.     NULL, 
  1066.     hInst, 
  1067.     NULL)) == NULL)
  1068.     {
  1069.     nError = -1;
  1070.     goto DONE_ROUTINE;
  1071.     }
  1072.  
  1073.     cox += DX_LOOPEDSPACING;
  1074.     coy += Size.cy + DY_TEXTSPACING + DY_LOOPEDSPACING;
  1075.     
  1076.     //Create line under WritePos
  1077.     if ((pFileInfo->hWndWritePosition_EDGE = CreateWindow(
  1078.     "STATIC", "", 
  1079.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1080.     cox, 
  1081.     coy - (DY_LOOPEDSPACING+DY_TEXTSPACING)/2,
  1082.     DX_LINEEDGE,
  1083.     DY_LINEEDGE,               
  1084.     hWnd,
  1085.     NULL, 
  1086.     hInst, 
  1087.     NULL)) == NULL)
  1088.     {
  1089.     nError = -1;
  1090.     goto DONE_ROUTINE;
  1091.     }
  1092.     
  1093.     #endif      
  1094.     
  1095.     //Set up the Freq Text
  1096.     if (!GetTextExtentPoint32(hDC, szFreq, strlen(szFreq), &Size))
  1097.     {
  1098.     nError = -1;
  1099.     goto DONE_ROUTINE;
  1100.     }
  1101.  
  1102.     // Make the frequency text there.
  1103.     if ((pFileInfo->hWndFreq_TXT = CreateWindow(
  1104.     "STATIC", 
  1105.     szFreq, 
  1106.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1107.     cox,
  1108.     coy,
  1109.     DX_FREQ_TXT, 
  1110.     Size.cy,                
  1111.     hWnd, 
  1112.     (HMENU)0, 
  1113.     hInst, 
  1114.     NULL)) == NULL)
  1115.     {
  1116.     nError = -1;
  1117.     goto DONE_ROUTINE;
  1118.     }   
  1119.  
  1120.     coy += Size.cy;
  1121.  
  1122.     // Make the frequency trackbar.
  1123.     if ((pFileInfo->hWndFreq_TB = CreateWindow(
  1124.     TRACKBAR_CLASS, 
  1125.     "", 
  1126.     WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTH, 
  1127.     cox,
  1128.     coy,
  1129.     DX_FREQ_TB, 
  1130.     DY_FREQ_TB,             
  1131.     hWnd, 
  1132.     (HMENU)(idBase+idFreqTB), 
  1133.     hInst, 
  1134.     NULL)) == NULL)
  1135.     {
  1136.     nError = -1;
  1137.     goto DONE_ROUTINE;
  1138.     }   
  1139.  
  1140.     // get the min and max range that the sound card supports.
  1141.     // If the buffer is in hardware query the card, else use
  1142.     // our ifdef'd values.
  1143.     if (pFileInfo->fHardware)
  1144.     {
  1145.         DSCAPS dsc;
  1146.         memset(&dsc, 0, sizeof(DSCAPS));
  1147.         dsc.dwSize = sizeof(DSCAPS);
  1148.         nError = IDirectSound_GetCaps(gpds, &dsc);
  1149.         Assert(nError == DS_OK);
  1150.         dwMinFreq = dsc.dwMinSecondarySampleRate;
  1151.         dwMaxFreq = dsc.dwMaxSecondarySampleRate;
  1152.     }
  1153.     else
  1154.     {    
  1155.         dwMinFreq = DSBFREQUENCY_MIN;    
  1156.         dwMaxFreq = DSBFREQUENCY_MAX;
  1157.     }
  1158.  
  1159.     SendMessage(pFileInfo->hWndFreq_TB, TBM_SETRANGEMIN, FALSE, dwMinFreq / FREQFACTOR );
  1160.     SendMessage(pFileInfo->hWndFreq_TB, TBM_SETRANGEMAX, FALSE, dwMaxFreq / FREQFACTOR );
  1161.     
  1162.     SendMessage(pFileInfo->hWndFreq_TB, TBM_SETPAGESIZE, 0, FREQPAGE );
  1163.     SendMessage(pFileInfo->hWndFreq_TB, TBM_SETPOS, TRUE, (dwFreq + FREQADD)/FREQFACTOR);
  1164.     pFileInfo->dwFreq = dwFreq;
  1165.  
  1166.  
  1167.     coy += DY_FREQ_TB+DY_PANSPACING;    
  1168.  
  1169.     if ((pFileInfo->hWndFreq_EDGE = CreateWindow(
  1170.     "STATIC", 
  1171.     "", 
  1172.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1173.     cox,
  1174.     coy - (DY_PANSPACING+DY_TEXTSPACING)/2,
  1175.     DX_LINEEDGE, 
  1176.     DY_LINEEDGE,                
  1177.     hWnd, 
  1178.     (HMENU)0, 
  1179.     hInst, 
  1180.     NULL)) == NULL)
  1181.     {
  1182.     nError = -1;
  1183.     goto DONE_ROUTINE;
  1184.     }   
  1185.  
  1186.     //Adjusts the relative position of the Text    
  1187.     coy -= (((DY_PANSPACING+DY_TEXTSPACING)/2)-((DY_LOOPEDSPACING+DY_TEXTSPACING)/2));
  1188.  
  1189.     if (!GetTextExtentPoint32(hDC, szPan, strlen(szPan), &Size))
  1190.     {
  1191.     nError = -1;
  1192.     goto DONE_ROUTINE;
  1193.     }
  1194.  
  1195.  
  1196.     // Make the pan text there.
  1197.     if ((pFileInfo->hWndPan_TXT = CreateWindow(
  1198.     "STATIC", 
  1199.     szPan, 
  1200.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1201.     cox,
  1202.     coy,
  1203.     DX_PAN_TXT, 
  1204.     Size.cy,                
  1205.     hWnd, 
  1206.     (HMENU)0, 
  1207.     hInst, 
  1208.     NULL)) == NULL)
  1209.     {
  1210.     nError = -1;
  1211.     goto DONE_ROUTINE;
  1212.     }   
  1213.  
  1214.     coy += Size.cy;
  1215.  
  1216.     // Make the pan trackbar.
  1217.     if ((pFileInfo->hWndPan_TB = CreateWindow(
  1218.     TRACKBAR_CLASS, 
  1219.     "", 
  1220.     WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_BOTH, 
  1221.     cox,
  1222.     coy,
  1223.     DX_PAN_TB, 
  1224.     DY_PAN_TB,              
  1225.     hWnd, 
  1226.     (HMENU)(idBase+idPanTB), 
  1227.     hInst, 
  1228.     NULL)) == NULL)
  1229.     {
  1230.     nError = -1;
  1231.     goto DONE_ROUTINE;
  1232.     }   
  1233.  
  1234.     SendMessage(pFileInfo->hWndPan_TB, TBM_SETRANGE, FALSE, MAKELONG(MINPAN_TB, MAXPAN_TB)); 
  1235.     SendMessage(pFileInfo->hWndPan_TB, TBM_SETPOS, TRUE, dwPan);
  1236.     SendMessage(pFileInfo->hWndPan_TB, TBM_SETPAGESIZE, 0, PANPAGE );
  1237.     pFileInfo->dwPan = dwPan;
  1238.  
  1239.  
  1240.     coy += DY_PAN_TB + DY_VOLSPACING;
  1241.  
  1242.     if ((pFileInfo->hWndPan_EDGE = CreateWindow(
  1243.     "STATIC", 
  1244.     "", 
  1245.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1246.     cox,
  1247.     coy - (DY_VOLSPACING+DY_TEXTSPACING)/2,
  1248.     DX_LINEEDGE, 
  1249.     DY_LINEEDGE,                
  1250.     hWnd, 
  1251.     (HMENU)0, 
  1252.     hInst, 
  1253.     NULL)) == NULL)
  1254.     {
  1255.     nError = -1;
  1256.     goto DONE_ROUTINE;
  1257.     }   
  1258.  
  1259.     //Adjusts the relative position of the Text
  1260.     coy -= (((DY_PANSPACING+DY_TEXTSPACING)/2)-((DY_LOOPEDSPACING+DY_TEXTSPACING)/2));
  1261.  
  1262.     if (!GetTextExtentPoint32(hDC, szVolume, strlen(szVolume), &Size))
  1263.     {
  1264.     nError = -1;
  1265.     goto DONE_ROUTINE;
  1266.     }
  1267.  
  1268.     // Make the volume text there.
  1269.     if ((pFileInfo->hWndVol_TXT = CreateWindow(
  1270.     "STATIC", 
  1271.     szVolume, 
  1272.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1273.     cox,
  1274.     coy,
  1275.     DX_VOL_TXT, 
  1276.     Size.cy,                
  1277.     hWnd, 
  1278.     (HMENU)idBase, 
  1279.     hInst, 
  1280.     NULL)) == NULL)
  1281.     {
  1282.     nError = -1;
  1283.     goto DONE_ROUTINE;
  1284.     }   
  1285.  
  1286.     coy += Size.cy;
  1287.  
  1288.     // Make the volume trackbars.
  1289.     // Create main volume bar.
  1290.     if ((pFileInfo->hWndVolM_TB = CreateWindow(
  1291.     TRACKBAR_CLASS, 
  1292.     "", 
  1293.     WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_BOTH, 
  1294.     cox,
  1295.     coy,
  1296.     DX_VOL_TB, 
  1297.     DY_VOL_TB,              
  1298.     hWnd, 
  1299.     (HMENU)(idBase+idVolMTB), 
  1300.     hInst, 
  1301.     NULL)) == NULL)
  1302.     {
  1303.     nError = -1;
  1304.     goto DONE_ROUTINE;
  1305.     }   
  1306.  
  1307.     SendMessage(pFileInfo->hWndVolM_TB, TBM_SETRANGE, FALSE, MAKELONG(MINVOL_TB, MAXVOL_TB)); 
  1308.     SendMessage(pFileInfo->hWndVolM_TB, TBM_SETPOS, TRUE, dwVol);
  1309.     pFileInfo->dwVol = MAXVOL_TB - dwVol;
  1310.  
  1311.  
  1312.  
  1313.     // Now the left volume.
  1314.     if ((pFileInfo->hWndVolL_TB = CreateWindow(
  1315.     TRACKBAR_CLASS, 
  1316.     "", 
  1317.     WS_CHILD | WS_VISIBLE |WS_DISABLED| TBS_VERT | TBS_BOTH, 
  1318.     cox+DX_VOL_TB+DX_VOLSPACING_TB,
  1319.     coy,
  1320.     DX_VOL_TB, 
  1321.     DY_VOL_TB,              
  1322.     hWnd, 
  1323.     (HMENU)(idBase+idVolLTB), 
  1324.     hInst, 
  1325.     NULL)) == NULL)
  1326.     {
  1327.     nError = -1;
  1328.     goto DONE_ROUTINE;
  1329.     }   
  1330.  
  1331.     SendMessage(pFileInfo->hWndVolL_TB, TBM_SETRANGE, FALSE, MAKELONG(MINVOL_TB, MAXVOL_TB)); 
  1332.     SendMessage(pFileInfo->hWndVolL_TB, TBM_SETPOS, TRUE, MAXVOL_TB);
  1333.  
  1334.     if ((pFileInfo->hWndVolL_TXT = CreateWindow(
  1335.     "STATIC", 
  1336.     "L", 
  1337.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1338.     cox+DX_VOL_TB*3/2+DX_VOLSPACING_TB/2,
  1339.     coy+DY_VOL_TB+DY_VOLSPACINGY,
  1340.     DX_VOLUMECHAR, 
  1341.     Size.cy,                
  1342.     hWnd, 
  1343.     (HMENU)0, 
  1344.     hInst, 
  1345.     NULL)) == NULL)
  1346.     {
  1347.     nError = -1;
  1348.     goto DONE_ROUTINE;
  1349.     }   
  1350.  
  1351.  
  1352.     // And right volume.
  1353.     if ((pFileInfo->hWndVolR_TB = CreateWindow(
  1354.     TRACKBAR_CLASS, 
  1355.     "", 
  1356.     WS_CHILD | WS_VISIBLE | WS_DISABLED | TBS_VERT | TBS_BOTH, 
  1357.     cox+DX_VOL_TB*2+DX_VOLSPACING_TB*2,
  1358.     coy,
  1359.     DX_VOL_TB, 
  1360.     DY_VOL_TB,              
  1361.     hWnd, 
  1362.     (HMENU)(idBase+idVolRTB), 
  1363.     hInst, 
  1364.     NULL)) == NULL)
  1365.     {
  1366.     nError = -1;
  1367.     goto DONE_ROUTINE;
  1368.     }   
  1369.  
  1370.     SendMessage(pFileInfo->hWndVolR_TB,
  1371.         TBM_SETRANGE, FALSE, MAKELONG(MINVOL_TB, MAXVOL_TB)); 
  1372.     SendMessage(pFileInfo->hWndVolR_TB,
  1373.         TBM_SETPOS, TRUE, MAXVOL_TB);
  1374.  
  1375.     if ((pFileInfo->hWndVolR_TXT = CreateWindow(
  1376.     "STATIC", 
  1377.     "R", 
  1378.     WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 
  1379.     cox+DX_VOL_TB*5/2+DX_VOLSPACING_TB/2+2,
  1380.                 // +2 to look nice.
  1381.     coy+DY_VOL_TB+DY_VOLSPACINGY,
  1382.     DX_VOLUMECHAR, 
  1383.     Size.cy,                
  1384.     hWnd, 
  1385.     (HMENU)0, 
  1386.     hInst, 
  1387.     NULL)) == NULL)
  1388.     {
  1389.     nError = -1;
  1390.     goto DONE_ROUTINE;
  1391.     }   
  1392.  
  1393.  
  1394.     coy += DY_VOL_TB + DY_BEFOREFIRSTBUTTON;    //Line under L & R
  1395.  
  1396.     if ((pFileInfo->hWndVol_EDGE = CreateWindow(
  1397.     "STATIC", 
  1398.     "", 
  1399.     WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 
  1400.     cox,
  1401.     coy - (DY_BEFOREFIRSTBUTTON)/2,
  1402.     DX_LINEEDGE, 
  1403.     DY_LINEEDGE,                
  1404.     hWnd, 
  1405.     (HMENU)0, 
  1406.     hInst, 
  1407.     NULL)) == NULL)
  1408.     {
  1409.     nError = -1;
  1410.     goto DONE_ROUTINE;
  1411.     }   
  1412.  
  1413.  
  1414.  
  1415.     if (!GetTextExtentPoint32(hDC, szPlay, strlen(szPlay), &Size))
  1416.     {
  1417.     nError = -1;
  1418.     goto DONE_ROUTINE;
  1419.     }
  1420.  
  1421.  
  1422.     //Create Play Button
  1423.     if ((pFileInfo->hWndPlay_BN = CreateWindow(
  1424.     "BUTTON", 
  1425.     szPlay, 
  1426.     WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 
  1427.     cox,
  1428.     coy,
  1429.     DX_BUTTONSPACING, 
  1430.     Size.cy + DY_BUTTONSPACING,             
  1431.     hWnd, 
  1432.     (HMENU)(idBase+idPlayBN), 
  1433.     hInst, 
  1434.     NULL)) == NULL)
  1435.     {
  1436.     nError = -1;
  1437.     goto DONE_ROUTINE;
  1438.     }       
  1439.  
  1440.     //coy += Size.cy + DY_BUTTONSPACING + DY_BETWEENBUTTONS;
  1441.     
  1442.     if (!GetTextExtentPoint32(hDC, szPlay, strlen(szPlay), &Size))
  1443.     {
  1444.     nError = -1;
  1445.     goto DONE_ROUTINE;
  1446.     }
  1447.      
  1448.     //Make Remove button
  1449.     if ((pFileInfo->hWndRemove_BN = CreateWindow(
  1450.     "BUTTON", 
  1451.     szRemove, 
  1452.     WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 
  1453.     cox + DX_BUTTONSPACING + DY_BETWEENBUTTONS,
  1454.     coy,
  1455.     DX_BUTTONSPACING, 
  1456.     Size.cy + DY_BUTTONSPACING,             
  1457.     hWnd, 
  1458.     (HMENU)(idBase+idRemoveBN), 
  1459.     hInst, 
  1460.     NULL)) == NULL)
  1461.     {
  1462.     nError = -1;
  1463.     goto DONE_ROUTINE;
  1464.     }       
  1465.  
  1466.     coy += Size.cy + DY_BUTTONSPACING+ DY_BETWEENBUTTONS;
  1467.  
  1468.  
  1469.     //Set up Looped Checkbox 
  1470.     if (!GetTextExtentPoint32(hDC, szLooped, strlen(szLooped), &Size))
  1471.     {
  1472.     nError = -1;
  1473.     goto DONE_ROUTINE;
  1474.     }
  1475.  
  1476.     //Create Looped Checkbox window
  1477.     if ((pFileInfo->hWndLooped_BN = CreateWindow(
  1478.     "BUTTON", 
  1479.     szLooped, 
  1480.     WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 
  1481.     cox,
  1482.     coy,
  1483.     DX_LOOPED_TXT, 
  1484.     Size.cy + DY_TEXTSPACING -2,               
  1485.     hWnd, 
  1486.     (HMENU)(idBase+idLoopedBN), 
  1487.     hInst, 
  1488.     NULL)) == NULL)
  1489.     {
  1490.     nError = -1;
  1491.     goto DONE_ROUTINE;
  1492.     }       
  1493.       
  1494.  
  1495.     // Don't need the between buttons spacing
  1496.     //  because there are no more controls.
  1497.     coy += Size.cy;// + DY_BUTTONSPACING; //+ DY_BETWEENBUTTONS;
  1498.  
  1499.     if ((pFileInfo->hWndWhole_EDGE = CreateWindow(
  1500.     "STATIC", 
  1501.     "", 
  1502.     WS_CHILD | WS_VISIBLE | SS_ETCHEDFRAME, 
  1503.     coxOld-DX_FRAMEEDGE,
  1504.     coyOld-DY_FRAMEEDGE,
  1505.     DX_CONTROLSPACING-DX_FRAMEEDGEINNER, 
  1506.     coy - coyOld + DY_FRAMEEDGE*2,              
  1507.     hWnd, 
  1508.     (HMENU)0, 
  1509.     hInst, 
  1510.     NULL)) == NULL)
  1511.     {
  1512.     nError = -1;
  1513.     goto DONE_ROUTINE;
  1514.     }   
  1515.  
  1516.     
  1517.     SetAllText(pFileInfo);
  1518.     UpdateLRVolume(pFileInfo);
  1519.  
  1520.     
  1521. DONE_ROUTINE:   
  1522.     if (hDC != NULL)
  1523.     {
  1524.     if (ReleaseDC(hWnd, hDC) == 0)
  1525.     {
  1526.     nError = -1;
  1527.     goto DONE_ROUTINE;
  1528.     }
  1529.     }
  1530.  
  1531.     return(nError);
  1532.  
  1533. }
  1534.  
  1535. // =======================================================================
  1536. /*  This will add to the linked list of FileInfo's.
  1537.     The FileInfo's keep track of the
  1538.     files loaded, and this is done in a linked list format
  1539.  
  1540.     Input:
  1541.     pFileInfoHead   -   Top of linked list.
  1542.     pFileInfo   -   Pointer to entry to add.
  1543.  
  1544.     Output:
  1545.     0 if successful, else the error code.
  1546.  
  1547. */      
  1548. // =======================================================================
  1549. int AddToList(FILEINFO *pFileInfoHead, FILEINFO *pFileInfo)
  1550. {
  1551.     pFileInfo->pNext = NULL;    
  1552.     pFileInfo->fPlaying = FALSE;
  1553.  
  1554.     while (pFileInfoHead->pNext != NULL)
  1555.     {
  1556.     pFileInfoHead = pFileInfoHead->pNext;
  1557.     }
  1558.  
  1559.     pFileInfoHead->pNext = pFileInfo;
  1560.  
  1561.     return(0);
  1562.  
  1563. }
  1564.  
  1565. // =======================================================================
  1566. /*  This routine will get the number of controls in the window.
  1567.     Can be used to determine new size of window.
  1568.  
  1569.     Input:
  1570.     pFileInfoHead           -   Header of linked list.
  1571.  
  1572.     Output:
  1573.     # of controls.
  1574. */
  1575. // =======================================================================
  1576. int GetNumControls( FILEINFO *pFileInfoHead )
  1577. {
  1578.  
  1579.     int cT  = 0;
  1580.  
  1581.     while (pFileInfoHead->pNext != NULL)
  1582.     {
  1583.     pFileInfoHead = pFileInfoHead->pNext;
  1584.     cT++;
  1585.     }
  1586.  
  1587.     return(cT);
  1588.  
  1589. }
  1590.  
  1591. // =======================================================================
  1592. /*  This routine will free the whole linked list in pFileInfoFirst,
  1593.     including all the
  1594.     memory used by the wave file, waveformatex structure, etc.
  1595. */
  1596. // =======================================================================
  1597. int FreeAllList(HWND hWnd, FILEINFO *pFileInfoFirst)
  1598. {
  1599.  
  1600.     FILEINFO        *pFileInfo, *pFileNext;
  1601.     UINT        cT;
  1602.  
  1603.     Assert(pFileInfoFirst != NULL);
  1604.     pFileInfo = pFileInfoFirst->pNext;
  1605.  
  1606.     while (pFileInfo != NULL)
  1607.     {
  1608.     ReleaseDirectSoundBuffer(pFileInfo);
  1609.     GlobalFreePtr(pFileInfo->pwfx);
  1610.     GlobalFreePtr(pFileInfo->pbData);
  1611.     pFileNext = pFileInfo->pNext;
  1612.     GlobalFreePtr(pFileInfo);
  1613.     pFileInfo = pFileNext;
  1614.     }
  1615.  
  1616.     for (cT=0; cT<MAXCONTROLS; cT++)
  1617.     rgfcoxAvail[cT] = FALSE;
  1618.  
  1619.  
  1620.  
  1621.     return(0);          
  1622.  
  1623.  
  1624. }
  1625.  
  1626. // =======================================================================
  1627. /*  This routine will remove an entry from the list, i.e. will remove
  1628.     pFileInfo and all its allocated memory from the list pointed by the header
  1629.     by pFileInfoHead
  1630.  
  1631.     Input:
  1632.     pFileInfo               -   Pointer to entry to remove.
  1633.     pFileInfoHead           -   Head, first entry.
  1634.  
  1635.     Output:
  1636.     0 if successful, else the error.
  1637.  
  1638. */
  1639. // =======================================================================
  1640. int RemoveFromList(FILEINFO *pFileInfo, FILEINFO *pFileInfoHead)
  1641. {
  1642.  
  1643.     FILEINFO        *pFileNext;
  1644.  
  1645.     Assert(pFileInfoHead != NULL);
  1646.  
  1647.     // This used to be pFileInfoHead != NULL
  1648.     while (pFileInfoHead->pNext != NULL)
  1649.     {
  1650.     if (pFileInfoHead->pNext == pFileInfo)
  1651.         {
  1652.         Assert(pFileInfo->cox/DX_CONTROLSPACING < MAXCONTROLS);
  1653.         rgfcoxAvail[pFileInfo->cox/DX_CONTROLSPACING] = FALSE;
  1654.        
  1655.         DestroyWindow(pFileInfo->hWndFileName_TXT); 
  1656.         DestroyWindow(pFileInfo->hWndFreq_TB);      
  1657.         DestroyWindow(pFileInfo->hWndFreq_TXT);     
  1658.         DestroyWindow(pFileInfo->hWndPan_TB);           
  1659.         DestroyWindow(pFileInfo->hWndPan_TXT);      
  1660.         DestroyWindow(pFileInfo->hWndVol_TXT);      
  1661.         DestroyWindow(pFileInfo->hWndVolL_TB);      
  1662.         DestroyWindow(pFileInfo->hWndVolR_TB);      
  1663.         DestroyWindow(pFileInfo->hWndVolM_TB);      
  1664.         DestroyWindow(pFileInfo->hWndLooped_BN);        
  1665.         DestroyWindow(pFileInfo->hWndPlay_BN);      
  1666.         DestroyWindow(pFileInfo->hWndRemove_BN);
  1667.         DestroyWindow(pFileInfo->hWndFileName_EDGE);
  1668.         DestroyWindow(pFileInfo->hWndLooped_EDGE);  
  1669.         DestroyWindow(pFileInfo->hWndFreq_EDGE);        
  1670.         DestroyWindow(pFileInfo->hWndPan_EDGE);     
  1671.         DestroyWindow(pFileInfo->hWndVol_EDGE);     
  1672.         DestroyWindow(pFileInfo->hWndWhole_EDGE);       
  1673.         DestroyWindow(pFileInfo->hWndVolL_TXT);     
  1674.         DestroyWindow(pFileInfo->hWndVolR_TXT);     
  1675.         #ifdef SHOWSTATUS
  1676.         DestroyWindow(pFileInfo->hWndStatus_TXT);
  1677.         DestroyWindow(pFileInfo->hWndStatus_EDGE);
  1678.         DestroyWindow(pFileInfo->hWndPlayPosition_TXT);
  1679.         DestroyWindow(pFileInfo->hWndPlayPosition_EDGE);
  1680.         DestroyWindow(pFileInfo->hWndWritePosition_TXT);
  1681.         DestroyWindow(pFileInfo->hWndWritePosition_EDGE);
  1682.         #endif
  1683.  
  1684.  
  1685.  
  1686.  
  1687.         GlobalFree(pFileInfoHead->pNext->pwfx);
  1688.         GlobalFree(pFileInfoHead->pNext->pbData);
  1689.         pFileNext = pFileInfoHead->pNext->pNext;
  1690.         GlobalFreePtr(pFileInfoHead->pNext);
  1691.         pFileInfoHead->pNext = pFileNext;                                                         
  1692.         break;
  1693.         }
  1694.     pFileInfoHead = pFileInfoHead->pNext;
  1695.     }
  1696.  
  1697.     return(0);
  1698. }
  1699.  
  1700. // =======================================================================
  1701. /*  This will pop up the open file dialog and allow the user to pick one file. 
  1702.     
  1703.     Input:  
  1704.     hWnd            -   Handle of parent window.
  1705.     pszFileName         -   String to store filename in, must be at least MAX_PATH long.
  1706.  
  1707.  
  1708.     Output:
  1709.     TRUE if a file was  picked successfully, else FALSE (user didn't pick a file)
  1710.  
  1711.  */
  1712. // =======================================================================
  1713. BOOL OpenFileDialog(HWND hWnd, LPSTR pszFileName, int *nFileName, LPBOOL lpfSticky)
  1714. {
  1715.     BOOL            fReturn,
  1716.             fValid;
  1717.     OPENFILENAME    ofn;                
  1718.  
  1719.     pszFileName[0]          = 0;
  1720.  
  1721.     ofn.lStructSize         = sizeof(ofn);
  1722.     ofn.hwndOwner           = hWnd;
  1723.     ofn.hInstance           = hInst;
  1724.     ofn.lpstrFilter         = "Wave Files\0*.wav\0All Files\0*.*\0\0";
  1725.     ofn.lpstrCustomFilter   = NULL;
  1726.     ofn.nMaxCustFilter      = 0;
  1727.     ofn.nFilterIndex        = 1;
  1728.     ofn.lpstrFile           = pszFileName;
  1729.     ofn.nMaxFile            = MAX_PATH;
  1730.     ofn.lpstrFileTitle      = NULL;
  1731.     ofn.nMaxFileTitle       = 0;
  1732.     ofn.lpstrInitialDir     = gszCDStartPath;
  1733.     ofn.lpstrTitle          = "File Open";
  1734.     ofn.Flags               = OFN_FILEMUSTEXIST | OFN_EXPLORER
  1735.                                 | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_HIDEREADONLY;
  1736.     ofn.nFileOffset         = 0;
  1737.     ofn.nFileExtension      = 0;
  1738.     ofn.lpstrDefExt         = "wav";
  1739.     ofn.lCustData           = (LONG)lpfSticky;
  1740.     ofn.lpfnHook            = FileOpenCustomTemplateDlgProc;
  1741.     ofn.lpTemplateName      = MAKEINTRESOURCE(IDD_FILEOPEN_NEST);
  1742.                 
  1743.     fValid = FALSE;
  1744.     do   {    
  1745.     
  1746.     if (fReturn = GetOpenFileName(&ofn))
  1747.     {                               
  1748.     // Set the start path for the next time this dialog is opened
  1749.         lstrcpy( gszCDStartPath, pszFileName );
  1750.         gszCDStartPath[ofn.nFileOffset] = '\0';
  1751.  
  1752.     fValid = IsValidWave(pszFileName);
  1753.     if (!fValid)
  1754.     {
  1755.     MessageBox(hWnd, "Wave files must be PCM format!",
  1756.            "Invalid Wave File", MB_OK|MB_ICONSTOP);
  1757.     }
  1758.     else
  1759.     *nFileName = ofn.nFileOffset;
  1760.     }
  1761.     else fValid = TRUE;         // Force break out of loop.
  1762.     
  1763.     } while (!fValid);
  1764.  
  1765.     return(fReturn);     
  1766.  
  1767. }
  1768.  
  1769. // =======================================================================
  1770. /*  This function will determine if the filename passed
  1771.     in is a valid wave for this
  1772.     app, that is a PCM wave.
  1773.  
  1774.     Input:
  1775.     pszFileName -   FileName to check.
  1776.  
  1777.     Output:
  1778.     FALSE if not a valid wave, TRUE if it is.
  1779.     
  1780. */
  1781. // =======================================================================
  1782. BOOL IsValidWave(LPSTR pszFileName)
  1783.     BOOL            fReturn     = FALSE;
  1784.     int             nError      = 0;
  1785.     HMMIO           hmmio;
  1786.     MMCKINFO        mmck;
  1787.     WAVEFORMATEX    *pwfx;
  1788.  
  1789.     if ((nError = WaveOpenFile(pszFileName, &hmmio, &pwfx, &mmck)) != 0)
  1790.     {       
  1791.     goto ERROR_IN_ROUTINE;
  1792.     }
  1793.  
  1794.     if (pwfx->wFormatTag != WAVE_FORMAT_PCM) 
  1795.     {
  1796.     goto ERROR_IN_ROUTINE;
  1797.     }
  1798.  
  1799.     WaveCloseReadFile(&hmmio, &pwfx);
  1800.  
  1801.     fReturn = TRUE;
  1802.  
  1803. ERROR_IN_ROUTINE:
  1804.     return(fReturn);    
  1805.  
  1806. }
  1807.  
  1808. // =======================================================================
  1809. // =======================================================================
  1810. BOOL UIMainWindowVSBHandler(HWND hWnd, WPARAM wParam, LPARAM lParam)
  1811. {
  1812.  
  1813.     FILEINFO    *pFileInfo;
  1814.     BOOL        fReturn             = FALSE;
  1815.  
  1816.     pFileInfo = FileInfoFirst.pNext;
  1817.  
  1818.     Assert(pFileInfo != NULL);
  1819.  
  1820.     while (pFileInfo != NULL)
  1821.     {
  1822.  
  1823.     if ((HWND)lParam == pFileInfo->hWndVolM_TB)
  1824.     {
  1825.     pFileInfo->dwVol = MAXVOL_TB -
  1826.     SendMessage(pFileInfo->hWndVolM_TB, TBM_GETPOS, 0, 0);
  1827.     ChangeOutputVol(pFileInfo);
  1828.     SetAllText(pFileInfo);
  1829.     UpdateLRVolume(pFileInfo);
  1830.     fReturn = TRUE;
  1831.     }
  1832.  
  1833.     pFileInfo = pFileInfo->pNext;
  1834.     
  1835.     }
  1836.  
  1837.     return (fReturn);
  1838.  
  1839. }
  1840.  
  1841.  
  1842. // =======================================================================
  1843. /*  This routine will handle all the calls to the WM_HSCROLL
  1844.     for the main window, that
  1845.     is, all the horizontal scrollbar (and trackbar) messages.
  1846.  
  1847.     Input:
  1848.     Standard parameters (minus the "message" parameter)
  1849.     for a window callback, though
  1850.     this is called from the window callback.
  1851.  
  1852.     Output:
  1853.     FALSE if the message isn't processed, else TRUE if it is.
  1854.     If FALSE, the
  1855.     return procedure should call the default windows procedure.
  1856. */
  1857. // =======================================================================
  1858. BOOL UIMainWindowHSBHandler(HWND hWnd, WPARAM wParam, LPARAM lParam)
  1859. {
  1860.  
  1861.     FILEINFO    *pFileInfo;
  1862.     BOOL        fReturn             = FALSE;
  1863.  
  1864.     pFileInfo = FileInfoFirst.pNext;
  1865.     
  1866.     Assert(pFileInfo != NULL);
  1867.  
  1868.     while (pFileInfo != NULL)
  1869.     {
  1870.  
  1871.     if ((HWND)lParam == pFileInfo->hWndFreq_TB)
  1872.     {
  1873.     pFileInfo->dwFreq = (SendMessage(pFileInfo->hWndFreq_TB,
  1874.             TBM_GETPOS, 0, 0) * FREQFACTOR) - FREQADD;
  1875.     ChangeOutputFreq(pFileInfo);
  1876.     SetAllText(pFileInfo);          
  1877.     fReturn = TRUE;
  1878.     }
  1879.  
  1880.     else if ((HWND)lParam == pFileInfo->hWndPan_TB)
  1881.     {
  1882.     pFileInfo->dwPan = SendMessage(pFileInfo->hWndPan_TB,
  1883.                TBM_GETPOS, 0, 0);
  1884.     ChangeOutputPan(pFileInfo);
  1885.     SetAllText(pFileInfo);
  1886.     UpdateLRVolume(pFileInfo);
  1887.     fReturn = TRUE;
  1888.     }
  1889.  
  1890.     pFileInfo = pFileInfo->pNext;
  1891.     
  1892.     }
  1893.  
  1894.     return (fReturn);
  1895.     
  1896.  
  1897.  
  1898. }
  1899.  
  1900. // =======================================================================
  1901. /*  This routine will handle all the calls to the WM_COMMAND
  1902.     for the main window.
  1903.  
  1904.     Input:
  1905.     Standard parameters (minus the "message" parameter)
  1906.     for a window callback, though
  1907.     this is called from the window callback.
  1908.  
  1909.     Output:
  1910.     FALSE if the message isn't processed, else TRUE if it is.
  1911.     If FALSE, the
  1912.     return procedure should call the default windows procedure.
  1913.     
  1914.  
  1915. */
  1916. // =======================================================================
  1917. BOOL UIMainWindowCMDHandler(HWND hWnd, WPARAM wParam, LPARAM lParam)
  1918. {
  1919.  
  1920.     BOOL        fReturn     = FALSE;
  1921.     FILEINFO        *pFileInfo;
  1922.     FILEINFO        *pFileInfoNext;
  1923.     DWORD       dwLooping;
  1924.  
  1925.     pFileInfo = FileInfoFirst.pNext;
  1926.     while (pFileInfo != NULL)
  1927.     {
  1928.     
  1929.     pFileInfoNext = pFileInfo->pNext;
  1930.     
  1931.     if ((HWND)lParam == pFileInfo->hWndLooped_BN)
  1932.     {
  1933.     pFileInfo->fLooped = SendMessage(pFileInfo->hWndLooped_BN,
  1934.              BM_GETCHECK, 0, 0);
  1935.     // If it is playing then reset the looping to be proper
  1936.     if( pFileInfo->fPlaying ) {
  1937.     if( pFileInfo->fLooped ) {
  1938.         dwLooping = DSBPLAY_LOOPING;
  1939.     } else {
  1940.             dwLooping = 0;
  1941.     }
  1942.         pFileInfo->pDSB->lpVtbl->Play(pFileInfo->pDSB,
  1943.                         0, 0, dwLooping );
  1944.     } 
  1945.     fReturn = TRUE;
  1946.     }
  1947.     else if ((HWND)lParam == pFileInfo->hWndPlay_BN)
  1948.     {
  1949.     if (pFileInfo->fPlaying)
  1950.     {
  1951.     if (StopDSound(hWnd, pFileInfo) == 0)
  1952.     {
  1953.         SendMessage((HWND)lParam,
  1954.         WM_SETTEXT, 0, (LPARAM)(LPCTSTR)szPlay);
  1955.  
  1956. #ifdef SHOWSTATUS
  1957.         UpdateStatus(pFileInfo, 0);
  1958. #endif
  1959.         
  1960.         fReturn = TRUE;
  1961.         break;
  1962.     }
  1963.     
  1964.     }
  1965.     else            
  1966.     {
  1967.     if (StartDSound(hWnd, pFileInfo) == 0)
  1968.     {
  1969.         SendMessage((HWND)lParam,
  1970.         WM_SETTEXT, 0, (LPARAM)(LPCTSTR)szStop);
  1971. #ifdef SHOWSTATUS
  1972.         UpdateStatus(pFileInfo, DSBSTATUS_PLAYING);
  1973. #endif
  1974.             
  1975.         fReturn = TRUE;
  1976.         break;
  1977.     }
  1978.     
  1979.     }
  1980.     fReturn = TRUE;
  1981.     }
  1982.     
  1983.     else if ((HWND)lParam == pFileInfo->hWndRemove_BN)
  1984.     {
  1985.     ReleaseDirectSoundBuffer(pFileInfo);
  1986.     RemoveFromList(pFileInfo, &FileInfoFirst);
  1987.         UpdateMainStatus();
  1988.     
  1989.     fReturn = TRUE;
  1990.     }
  1991.     
  1992.  
  1993.     pFileInfo = pFileInfoNext;
  1994.     
  1995.     }
  1996.     
  1997.     if (!fReturn)
  1998.     {
  1999.  
  2000.     switch(wParam)
  2001.     {
  2002.  
  2003.     case IDPD_FILE_EXIT:    
  2004.     PostMessage(hWnd, WM_CLOSE, 0, 0);
  2005.     break;
  2006.  
  2007.     case IDPD_FILE_OPEN:
  2008.     PD_FileOpen(hWnd);
  2009.     break;
  2010.         
  2011.     case IDPD_HELP_ABOUT:
  2012.     DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT),
  2013.           hWnd, (DLGPROC)DLGHelpAbout);
  2014.     break;
  2015.  
  2016.     case IDPD_OPTIONS_OUTPUTTYPE:
  2017.     DialogBox(hInst, MAKEINTRESOURCE(IDD_OUTPUTBUFFERTYPE),
  2018.           hWnd, (DLGPROC)DLGOutputBufferType);
  2019.     break;
  2020.  
  2021.     case IDPD_CHECKLATENCY:
  2022.     StopAllDSounds(hWnd, &FileInfoFirst);
  2023.     // Now fake that we're on in each voice so the
  2024.     //timer will update the 
  2025.     // strings in the window.
  2026.     pFileInfo = FileInfoFirst.pNext;
  2027.     while (pFileInfo != NULL)
  2028.     {                                           
  2029.         pFileInfo->fPlaying = TRUE;
  2030.         pFileInfo = pFileInfo->pNext;       
  2031.     }
  2032.  
  2033.     DialogBox(hInst, MAKEINTRESOURCE(IDD_CHECKLATENCY),
  2034.           hWnd, (DLGPROC)DLGCheckLatency);
  2035.     break;
  2036.  
  2037.         case IDPD_ENUMDRIVERS:
  2038.         fEnumDrivers = !fEnumDrivers;
  2039.         if( fEnumDrivers )
  2040.             {
  2041.             MessageBox( hWnd,
  2042.             "Drivers will not be enumerated until DSSHOW is run again.",
  2043.             szAppName, MB_OK );
  2044.             }
  2045.         break;
  2046.  
  2047.     default:
  2048.     return(FALSE);
  2049.     
  2050.     }
  2051.     }
  2052.  
  2053.     return(TRUE);
  2054.  
  2055.  
  2056. }
  2057.  
  2058. // =======================================================================
  2059. /*  This routine will handle the timer messages.
  2060.  
  2061.     Input:
  2062.     Standard input.
  2063.  
  2064.     Output: 
  2065.     TRUE if processed message, otherwise FALSE
  2066. */
  2067. // =======================================================================
  2068. BOOL UIMainWindowTimerHandler(HWND hWnd, WPARAM wParam, LPARAM lParam)
  2069. {
  2070.     FILEINFO        *pFileInfo;
  2071.     BOOL            fReturn             = FALSE;
  2072.     DWORD           dwStatus            = 0;
  2073.  
  2074.     for (pFileInfo = FileInfoFirst.pNext; pFileInfo != NULL; pFileInfo = pFileInfo->pNext)
  2075.     {
  2076.     HRESULT hr;
  2077.  
  2078.     hr = IDirectSoundBuffer_GetStatus(pFileInfo->pDSB, &dwStatus);
  2079.     if (DS_OK != hr) continue;
  2080.  
  2081.     if (dwStatus & DSBSTATUS_BUFFERLOST) {
  2082.     LPBYTE pbData, pbData2;
  2083.     DWORD  dwLength, dwLength2;
  2084.     
  2085.     //
  2086.     //  Restore the buffer, rewrite data, and play
  2087.     //
  2088.     hr = IDirectSoundBuffer_Restore(pFileInfo->pDSB);
  2089.     if (DS_OK == hr) {
  2090.  
  2091.     hr = IDirectSoundBuffer_Lock(pFileInfo->pDSB, 0,
  2092.              pFileInfo->cbSize,
  2093.              &pbData, &dwLength,
  2094.              &pbData2, &dwLength2,
  2095.              0);
  2096.  
  2097.     if (DS_OK == hr) {
  2098.     
  2099.         Assert(pbData != NULL);
  2100.         Assert(pFileInfo->pbData != NULL);
  2101.         memcpy(pbData, pFileInfo->pbData, pFileInfo->cbSize);
  2102.  
  2103.         hr = IDirectSoundBuffer_Unlock(pFileInfo->pDSB,
  2104.                        pbData, dwLength,
  2105.                        NULL, 0);
  2106.  
  2107.         if (DS_OK == hr) {
  2108.  
  2109.         if (pFileInfo->fPlaying) {
  2110.         if (pFileInfo->fLooped) {
  2111.         IDirectSoundBuffer_Play( pFileInfo->pDSB, 0, 0,
  2112.                      DSBPLAY_LOOPING );
  2113.         } else {
  2114.         IDirectSoundBuffer_Play( pFileInfo->pDSB, 0, 0,
  2115.                      0 );
  2116.         }
  2117.         }
  2118.  
  2119.         IDirectSoundBuffer_GetStatus(pFileInfo->pDSB, &dwStatus);
  2120.  
  2121.         }
  2122.     }
  2123.     }
  2124.     }
  2125.  
  2126. #ifdef SHOWSTATUS
  2127.     UpdateStatus(pFileInfo, dwStatus);
  2128. #endif
  2129.  
  2130.     if (!(dwStatus & DSBSTATUS_BUFFERLOST))
  2131.     {
  2132.     if ((pFileInfo->fPlaying) && (!(dwStatus & DSBSTATUS_PLAYING)) )
  2133.     {
  2134.     if (StopDSound(hWnd, pFileInfo) == 0)
  2135.     {
  2136.         SendMessage(pFileInfo->hWndPlay_BN,
  2137.         WM_SETTEXT, 0, (LPARAM)(LPCTSTR)szPlay);
  2138.     }
  2139.     }
  2140.     }
  2141.  
  2142.     pFileInfo->fLost = dwStatus & DSBSTATUS_BUFFERLOST;
  2143.  
  2144.     fReturn = TRUE;
  2145.     }
  2146.  
  2147.     return (fReturn);
  2148.  
  2149. }   
  2150.  
  2151.  
  2152.  
  2153. // =======================================================================
  2154. /*  This routine will start a sound to be played.  
  2155.     Input:
  2156.     hWnd        -   Of parent window.
  2157.     pFileInfo   -   Pointer to file to start,
  2158.         which is loaded and the
  2159.         data is filled in the structure,
  2160.         such as pbData, 
  2161.         etc.
  2162.  
  2163.     Output:
  2164.     0 if successful, else the error code.
  2165. */
  2166. // =======================================================================
  2167. int StartDSound(HWND hWnd, FILEINFO *pFileInfo)
  2168. {
  2169.     HRESULT     hr              = 0;
  2170.     DWORD           dwLooped;
  2171.     DWORD           dwStatus                = 0;
  2172.  
  2173.     // Already playing?
  2174.  
  2175.     // Start sound here....
  2176.     dwLooped = 0;
  2177.     if (pFileInfo->fLooped) {
  2178.     dwLooped = DSBPLAY_LOOPING;
  2179.     }
  2180.             
  2181.  
  2182.     if ((hr = pFileInfo->pDSB->lpVtbl->GetStatus(pFileInfo->pDSB,
  2183.              &dwStatus)) == 0)
  2184.     {
  2185.     if ((dwStatus&DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
  2186.     {
  2187.     // Don't bother playing, just restart
  2188.     if ((hr = pFileInfo->pDSB->lpVtbl->SetCurrentPosition(
  2189.         pFileInfo->pDSB, 0)) != 0)
  2190.     {
  2191.     MessageBox(hWnd, "Cannot set current position",
  2192.            "Direct Sound Error", MB_OK);
  2193.     }
  2194.     }
  2195.     // Yes gotos are bad but this is real life not school.
  2196.     else goto PLAY_THE_THING;           
  2197.     }
  2198.     
  2199.     else
  2200.     {
  2201. PLAY_THE_THING:
  2202.     if ((hr = pFileInfo->pDSB->lpVtbl->Play(pFileInfo->pDSB,
  2203.                         0, 0, dwLooped)) != 0)
  2204.     {
  2205.     MessageBox(hWnd, "Cannot start playing",
  2206.        "Direct Sound Error", MB_OK);
  2207.     }
  2208.     else
  2209.     pFileInfo->fPlaying = TRUE;
  2210.     }
  2211.  
  2212.     return(hr);
  2213.  
  2214.  
  2215. }
  2216.  
  2217. // =======================================================================
  2218. /*  This routine will stop a sound which is playing.
  2219.  
  2220.     Input:
  2221.     hWnd        - Of parent window.
  2222.     pFileInfo       - Pointer to file to stop playing.
  2223.  
  2224.     Output:
  2225.     0 if successful, else the error code.
  2226.  
  2227. */
  2228. // =======================================================================
  2229. int StopDSound(HWND hWnd, FILEINFO *pFileInfo)
  2230. {
  2231.     HRESULT     hr          = 0;
  2232.  
  2233.     if (!pFileInfo->fPlaying)
  2234.     return(0);
  2235.        
  2236.  
  2237.     // Stop sound here...
  2238.     if ((hr = pFileInfo->pDSB->lpVtbl->Stop(pFileInfo->pDSB)) != 0) 
  2239.     {
  2240.     MessageBox(hWnd, "Cannot stop sound",
  2241.        "Direct Sound Error", MB_OK);        
  2242.     }
  2243.     else
  2244.     pFileInfo->fPlaying = FALSE;    
  2245.  
  2246.     return(hr);
  2247.  
  2248. }
  2249.  
  2250. // =======================================================================
  2251. /*  This routine will stop all the sounds which are playing.
  2252.  
  2253.     Input:
  2254.     hWnd        - Of parent window.
  2255.     pFileInfo   - Pointer to file to stop playing.
  2256.         (i.e. the head)
  2257.  
  2258.     Output:
  2259.     0 if successful, else the error code.
  2260.  
  2261. */
  2262. // =======================================================================
  2263. int StopAllDSounds(HWND hWnd, FILEINFO *pFileInfo)
  2264. {
  2265.     while (pFileInfo->pNext != NULL)
  2266.     {
  2267.     StopDSound(hWnd, pFileInfo->pNext);
  2268.     pFileInfo = pFileInfo->pNext;       
  2269.     }
  2270.  
  2271.     return(0);
  2272.  
  2273. }
  2274.  
  2275.  
  2276.  
  2277. // =======================================================================
  2278. /*  This routine will set the freq, vol and pan slider text
  2279.     according to the value 
  2280.     passed in.
  2281.  
  2282.     Input:
  2283.     pFileInfo   -   File pointer to set frequency for.
  2284.  
  2285.     The dwFreq in the pFileInfo structure must be set.
  2286.     This also uses the window handle
  2287.     in the pFileInfo structure.
  2288.     
  2289.     Output:
  2290.     None.
  2291. */
  2292. // =======================================================================
  2293. void SetAllText(FILEINFO    *pFileInfo)
  2294. {
  2295.     char            szBufT[128];
  2296.  
  2297.     sprintf(szBufT, "%s: %lu Hz     ",
  2298.     szFreq, pFileInfo->dwFreq);
  2299.     SetWindowText(pFileInfo->hWndFreq_TXT, szBufT);
  2300.  
  2301.     // Change PAN val to show full range
  2302.     sprintf(szBufT, "%s: %ld", szPan,
  2303.     (((LONG)(pFileInfo->dwPan) + SHIFTPAN_TB) * MULTPAN_TB ) );
  2304.     SetWindowText(pFileInfo->hWndPan_TXT, szBufT);
  2305.  
  2306.     // Change VOLUME val to show full range
  2307.     sprintf(szBufT, "%s: %ld", szVolume,
  2308.     (((LONG)(pFileInfo->dwVol) + SHIFTVOL_TB) * MULTVOL_TB ));
  2309.     SetWindowText(pFileInfo->hWndVol_TXT, szBufT);
  2310.  
  2311.  
  2312. }
  2313.  
  2314. // =======================================================================
  2315. /*  This routine will update the left and right
  2316.     volume according to main volume 
  2317.     and pan.
  2318.  
  2319.     Input:
  2320.     pFileInfo   - Pointer to fileinfo to update.
  2321.  
  2322.     Output:
  2323.     Nothing worth using.
  2324. */
  2325. // =======================================================================
  2326. void UpdateLRVolume(FILEINFO *pFileInfo)
  2327. {
  2328.  
  2329.     int             volLeft, volRight;
  2330.  
  2331.     if (pFileInfo->dwPan < MIDPAN_TB)
  2332.     {
  2333.     volLeft = pFileInfo->dwVol;
  2334.     volRight = (((int)pFileInfo->dwPan)
  2335.         *(int)pFileInfo->dwVol)/((int)MIDPAN_TB);
  2336.     }
  2337.     else
  2338.     {
  2339.     volLeft = ((((int)pFileInfo->dwPan - MAXPAN_TB)*-1)
  2340.        *(int)pFileInfo->dwVol)/((int)MIDPAN_TB);
  2341.     volRight = pFileInfo->dwVol;
  2342.     }
  2343.  
  2344.     
  2345.  
  2346.     SendMessage(pFileInfo->hWndVolL_TB, TBM_SETPOS, TRUE, MAXVOL_TB-volLeft);
  2347.     SendMessage(pFileInfo->hWndVolR_TB, TBM_SETPOS, TRUE, MAXVOL_TB-volRight);
  2348.     
  2349.     
  2350.  
  2351. }
  2352.  
  2353. // =======================================================================
  2354. /*  This will change the output panning position for a certain FILEINFO.
  2355.     This is 
  2356.     done by sending messages to the direct sound driver 
  2357.  
  2358.     Input:  
  2359.     pFileInfo   -   FileInfo to set.  This must contain the
  2360.         panning value to set.
  2361.  
  2362.     Output:
  2363.     0 if successful, else the error code.
  2364.  
  2365. */
  2366. // =======================================================================
  2367. int ChangeOutputPan(FILEINFO *pFileInfo)
  2368. {
  2369.  
  2370.     HRESULT     hr      = 0;
  2371.  
  2372.     // Change PAN val  since TB does not go full range
  2373.     if ((hr = pFileInfo->pDSB->lpVtbl->SetPan(pFileInfo->pDSB,
  2374.     (((pFileInfo->dwPan) + SHIFTPAN_TB) * MULTPAN_TB) )) != 0)
  2375.     {
  2376.     goto ERROR_DONE_ROUTINE;
  2377.     }
  2378.  
  2379. ERROR_DONE_ROUTINE:
  2380.     return(hr);
  2381.  
  2382. }
  2383.  
  2384. // =======================================================================
  2385. /*  This will change the output freq for a certain FILEINFO.  This is 
  2386.     done by sending messages to the direct sound driver 
  2387.  
  2388.     Input:  
  2389.     pFileInfo                   -   FileInfo to set.  This must contain the
  2390.                     freq value to set.
  2391.  
  2392.     Output:
  2393.     0 if successful, else the error code.
  2394.  
  2395. */
  2396. // =======================================================================
  2397. int ChangeOutputFreq(FILEINFO *pFileInfo)
  2398. {
  2399.  
  2400.     HRESULT     hr      = 0;
  2401.  
  2402.     if ((hr = pFileInfo->pDSB->lpVtbl->SetFrequency(pFileInfo->pDSB, pFileInfo->dwFreq)) != 0)
  2403.     {
  2404.     goto ERROR_DONE_ROUTINE;
  2405.     }
  2406.  
  2407. ERROR_DONE_ROUTINE:
  2408.     return(hr);
  2409.  
  2410. }
  2411.  
  2412.  
  2413.  
  2414. // =======================================================================
  2415. /*  This will change the output vol for a certain FILEINFO.  This is 
  2416.     done by sending messages to the direct sound driver 
  2417.  
  2418.     Input:  
  2419.     pFileInfo                   -   FileInfo to set.  This must contain the
  2420.                     freq value to set.
  2421.  
  2422.     Output:
  2423.     0 if successful, else the error code.
  2424.  
  2425. */
  2426. // =======================================================================
  2427. int ChangeOutputVol(FILEINFO *pFileInfo)
  2428. {
  2429.  
  2430.     HRESULT     hr      = 0;
  2431.  
  2432.     // Shift VOLUME val by 4 bits since TB does not go full range
  2433.     if ((hr = pFileInfo->pDSB->lpVtbl->SetVolume(pFileInfo->pDSB,
  2434.     (((pFileInfo->dwVol) + SHIFTVOL_TB) * MULTVOL_TB) )) != 0)
  2435.     {
  2436.     goto ERROR_DONE_ROUTINE;
  2437.     }
  2438.  
  2439. ERROR_DONE_ROUTINE:
  2440.     return(hr);
  2441.  
  2442. }
  2443.  
  2444.  
  2445. // =======================================================================
  2446. /*  This is the dialog box handler for the check latency dialog box.
  2447.  
  2448.     Input:
  2449.     Standard dialog box input.
  2450.  
  2451.     Output:
  2452.     Standard dialog box output.
  2453.  
  2454. */
  2455. // =======================================================================
  2456. long FAR PASCAL DLGCheckLatency(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2457. {
  2458.  
  2459.     static HWND     hWndFiles_LB;
  2460.     FILEINFO        *pFileInfo              = NULL;
  2461.     int         nSelected;
  2462.     int         cT;
  2463.  
  2464.  
  2465.     switch(uMsg)
  2466.     {
  2467.     case WM_INITDIALOG:
  2468.     hWndFiles_LB = GetDlgItem(hWnd, IDC_FILES_LB);
  2469.     
  2470.     pFileInfo = FileInfoFirst.pNext;
  2471.     while (pFileInfo != NULL)
  2472.     {               
  2473.     SendMessage(hWndFiles_LB,
  2474.         LB_ADDSTRING,
  2475.         0,
  2476.         (LPARAM)(pFileInfo->szFileName
  2477.              + pFileInfo->nFileName));
  2478.         pFileInfo = pFileInfo->pNext;       
  2479.     }
  2480.  
  2481.     break;      
  2482.     
  2483.     case WM_COMMAND:
  2484.     switch(wParam)
  2485.     {
  2486.     case ID_DONE:                   
  2487.         PostMessage(hWnd, WM_CLOSE, 0, 0);
  2488.         break;
  2489.         
  2490.     case ID_PLAY:                       
  2491.         if ((nSelected = SendMessage(hWndFiles_LB,
  2492.              LB_GETCURSEL, 0, 0))
  2493.         != LB_ERR)
  2494.         {
  2495.         for (cT=0, pFileInfo = FileInfoFirst.pNext;
  2496.         pFileInfo != NULL;
  2497.         pFileInfo = pFileInfo->pNext, cT++)
  2498.         {
  2499.         if (cT == nSelected)
  2500.         {
  2501.         StartDSound(hWnd, pFileInfo);
  2502.         break;
  2503.         }
  2504.         }
  2505.         
  2506.         }
  2507.             
  2508.         break;
  2509.             
  2510.     case ID_STOP:
  2511.         StopAllDSounds(hWnd, &FileInfoFirst);
  2512.         break;
  2513.         
  2514.     default:
  2515.         break;
  2516.         
  2517.     }
  2518.     break;
  2519.  
  2520.     case WM_CLOSE:
  2521.     StopAllDSounds(hWnd, &FileInfoFirst);
  2522.     EndDialog(hWnd, 0);
  2523.     break;
  2524.  
  2525.     default:
  2526.     return(0);
  2527.     break;               
  2528.     
  2529.     }
  2530.     
  2531.     return(1);
  2532.  
  2533. }
  2534.  
  2535.  
  2536. // =======================================================================
  2537. /*  The help about dialog procedure.  
  2538.     
  2539.     Input:
  2540.     Standard windows dialog procedure.
  2541.  
  2542.     Output:
  2543.     Standard windows dialog procedure.
  2544.  
  2545. */
  2546. // =======================================================================
  2547. long FAR PASCAL DLGHelpAbout(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2548. {
  2549.     switch(uMsg)
  2550.     {
  2551.     case WM_INITDIALOG:
  2552.     break;      
  2553.     
  2554.     case WM_COMMAND:
  2555.     switch(wParam)
  2556.     {
  2557.     case ID_OK:                 
  2558.         PostMessage(hWnd, WM_CLOSE, 0, 0);
  2559.         break;
  2560.         
  2561.     default:
  2562.         break;
  2563.         
  2564.     }
  2565.     break;
  2566.     
  2567.     case WM_CLOSE:
  2568.     EndDialog(hWnd, 0);
  2569.     break;
  2570.  
  2571.     default:
  2572.     return(0);
  2573.     break;               
  2574.  
  2575.     }
  2576.     
  2577.     return(1);
  2578.  
  2579. }
  2580.  
  2581.  
  2582. // =======================================================================
  2583. /*  The help about dialog procedure.  
  2584.     
  2585.     Input:
  2586.     Standard windows dialog procedure.
  2587.  
  2588.     Output:
  2589.     Standard windows dialog procedure.
  2590.  
  2591. */
  2592. // =======================================================================
  2593. long FAR PASCAL DLGOutputBufferType(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
  2594. {
  2595.  
  2596.     static HWND     hWndFormats_LB          = NULL;
  2597.     int         cT;
  2598.     int         nSelection;
  2599.  
  2600.          
  2601.     switch(uMsg)
  2602.     {
  2603.     case WM_INITDIALOG:
  2604.     // Get the windows we need.
  2605.     hWndFormats_LB = GetDlgItem(hWnd, IDC_FORMATS);
  2606.     
  2607.     // Put the strings in the list box.
  2608.     for (cT=0; cT<C_DROPDOWNPCMFORMATS; cT++)
  2609.     SendMessage(hWndFormats_LB,
  2610.         LB_ADDSTRING, 0, (LPARAM)rgszTypes[cT]);
  2611.  
  2612.     // Get the current format and highlight it in the list box.
  2613.     if ((nSelection = FormatToIndex(hWnd, &FileInfoFirst)) != LB_ERR)
  2614.     {
  2615.     SendMessage(hWndFormats_LB, LB_SETCURSEL, nSelection, 0);
  2616.     }
  2617.  
  2618.  
  2619.     break;      
  2620.     
  2621.     case WM_COMMAND:
  2622.         switch(LOWORD(wParam))
  2623.     {
  2624.         case IDC_FORMATS:
  2625.             if( HIWORD( wParam ) == LBN_DBLCLK )
  2626.             {
  2627.             SendMessage( hWnd, WM_COMMAND, MAKEWPARAM( ID_OK, 0 ),
  2628.                                     0L );
  2629.             }
  2630.             break;
  2631.  
  2632.         case ID_OK:
  2633.         if ((nSelection = SendMessage(hWndFormats_LB,
  2634.         LB_GETCURSEL, 0, 0)) != LB_ERR)
  2635.         {
  2636.         if (IndexToFormat(hWnd, &FileInfoFirst, nSelection)
  2637.             == 0)
  2638.         PostMessage(hWnd, WM_CLOSE, 0, 0);
  2639.         }
  2640.         break;
  2641.         
  2642.     case ID_CANCEL:                 
  2643.         PostMessage(hWnd, WM_CLOSE, 0, 0);
  2644.         break;
  2645.  
  2646.     case ID_APPLY:                  
  2647.         if ((nSelection = SendMessage(hWndFormats_LB,
  2648.         LB_GETCURSEL, 0, 0)) != LB_ERR)
  2649.         IndexToFormat(hWnd, &FileInfoFirst, nSelection);
  2650.             
  2651.         break;
  2652.  
  2653.  
  2654.     default:
  2655.         break;
  2656.  
  2657.     }
  2658.     break;
  2659.  
  2660.     case WM_CLOSE:
  2661.     EndDialog(hWnd, 0);
  2662.     break;
  2663.  
  2664.     default:
  2665.     return(0);
  2666.     break;               
  2667.  
  2668.     }
  2669.         
  2670.     return(1);
  2671.  
  2672. }
  2673.  
  2674. // =======================================================================
  2675. /*  This routine will determine the output format in
  2676.     terms of an integer from the
  2677.     current output rate, type, etc.
  2678.     stored in the direct sound routines.   Integer
  2679.     values designate the string # in rgszTypes,
  2680.     i.e. index 0 is 8000kHz, 8 bit mono, 
  2681.     etc...
  2682.  
  2683.     Input:
  2684.     hWnd    - Handle of the current window.
  2685.     pFileInfo   - Pointer to the file info to retrieve the format for.
  2686.  
  2687.     Output:
  2688.     The index of the format, LB_ERR if undetermined.
  2689.  
  2690. */
  2691. // =======================================================================
  2692. int FormatToIndex(HWND hWnd, FILEINFO *pFileInfo)
  2693. {
  2694.  
  2695.     WAVEFORMATEX    wfx;
  2696.     DWORD       dwWaveStyle;
  2697.     DWORD       dwSize;
  2698.     int         nError              = 0;
  2699.  
  2700.     // Get the format.
  2701.     if ((nError = pFileInfo->pDSB->lpVtbl->GetFormat(pFileInfo->pDSB,
  2702.         &wfx, sizeof(wfx), &dwSize)) != 0)
  2703.     {
  2704.     goto ERROR_IN_ROUTINE;
  2705.     }
  2706.     if( dwSize > sizeof( wfx ) ) {
  2707.     nError = DSERR_GENERIC;
  2708.     goto ERROR_IN_ROUTINE;
  2709.     }
  2710.  
  2711.  
  2712.     // Change wfx to an integer.
  2713.     // Assume theres an error and check all parameters to 
  2714.     // see if its valid.
  2715.     nError = LB_ERR;
  2716.     dwWaveStyle = 0;
  2717.  
  2718.     if (wfx.wFormatTag != WAVE_FORMAT_PCM)
  2719.        goto ERROR_IN_ROUTINE;
  2720.  
  2721.     // Check the channels
  2722.     switch (wfx.nChannels)
  2723.     {
  2724.     case 1:
  2725.         break;
  2726.  
  2727.     case 2:
  2728.         dwWaveStyle |= 1;
  2729.         break;
  2730.  
  2731.     default:
  2732.         goto ERROR_IN_ROUTINE;
  2733.     }
  2734.  
  2735.     // Check the bits...
  2736.     switch (wfx.wBitsPerSample)
  2737.     {
  2738.     case 8:
  2739.         break;
  2740.  
  2741.     case 16:
  2742.         dwWaveStyle |= 2;
  2743.         break;
  2744.  
  2745.     default:
  2746.         goto ERROR_IN_ROUTINE;
  2747.     }
  2748.  
  2749.     // Check the rate.
  2750.     switch(wfx.nSamplesPerSec)
  2751.     {
  2752.     case 8000:
  2753.         break;
  2754.  
  2755.     case 11025:
  2756.         dwWaveStyle |= 4;
  2757.         break;
  2758.  
  2759.     case 22050:
  2760.         dwWaveStyle |= 8;
  2761.         break;
  2762.  
  2763.     case 44100:
  2764.         dwWaveStyle |= 12;
  2765.         break;
  2766.  
  2767.     default:
  2768.         goto ERROR_IN_ROUTINE;
  2769.     }
  2770.  
  2771.     nError = (int)dwWaveStyle;
  2772.  
  2773. ERROR_IN_ROUTINE:
  2774.     return(nError);
  2775. }
  2776.  
  2777.  
  2778. // =======================================================================
  2779. /*  This will convert an index (from a list box for instance)
  2780.     to a format by passing
  2781.     in the format to direct sound.
  2782.  
  2783.     Input:
  2784.     hWnd        -   Handle to window.
  2785.     pFileInfo   -   Pointer to current file info.
  2786.     index       -   Index value to convert to a
  2787.             waveformat structure.
  2788.  
  2789.     Output:
  2790.     0 if successful, else the error code.
  2791. */
  2792. // =======================================================================
  2793. int IndexToFormat(HWND hWnd, FILEINFO *pFileInfo, int index)
  2794. {
  2795.  
  2796.     int         nError      = 0;
  2797.  
  2798.  
  2799.     pFileInfo->pwfx->wFormatTag = WAVE_FORMAT_PCM;
  2800.  
  2801.     pFileInfo->pwfx->nChannels = 2;                                     // Assume stereo.
  2802.     if ((index%2) == 0)
  2803.     pFileInfo->pwfx->nChannels = 1;                                 // Its mono.
  2804.     
  2805.     // Assume 16 bit    
  2806.     pFileInfo->pwfx->nBlockAlign = 2*pFileInfo->pwfx->nChannels;
  2807.     pFileInfo->pwfx->wBitsPerSample = 16;
  2808.     if ((index%4) < 2) {
  2809.     // Its 8 bit.
  2810.     pFileInfo->pwfx->nBlockAlign = 1*pFileInfo->pwfx->nChannels;
  2811.     pFileInfo->pwfx->wBitsPerSample = 8;
  2812.     }
  2813.     
  2814.     pFileInfo->pwfx->nSamplesPerSec = 44100;    // Assume 44.1 kHz
  2815.     if (index < 4)
  2816.     pFileInfo->pwfx->nSamplesPerSec = 8000;
  2817.     else if (index < 8)
  2818.     pFileInfo->pwfx->nSamplesPerSec = 11025;
  2819.     else if (index < 12)
  2820.     pFileInfo->pwfx->nSamplesPerSec = 22050;
  2821.     
  2822.     
  2823.     pFileInfo->pwfx->nAvgBytesPerSec = pFileInfo->pwfx->nSamplesPerSec *
  2824.                pFileInfo->pwfx->nBlockAlign;                                        
  2825.     pFileInfo->pwfx->cbSize = 0;
  2826.  
  2827.     if ((nError = pFileInfo->pDSB->lpVtbl->SetFormat(pFileInfo->pDSB,
  2828.             pFileInfo->pwfx)) != DS_OK)         {
  2829.     MessageBox(hWnd, "Cannot set format buffer",
  2830.        "Direct Sound Error", MB_OK);
  2831.     goto ERROR_DONE_ROUTINE;
  2832.  
  2833.     }
  2834.  
  2835. ERROR_DONE_ROUTINE:
  2836.     return(nError);
  2837.  
  2838. }
  2839.  
  2840.  
  2841. // =======================================================================
  2842. /* GetMediaStartPath()                                                      */
  2843. /*                                                                          */
  2844. /*   This helper function attempts to get the media directory for Direct3D, */
  2845. /* which is where all the installed DX wave files go. If it can't find that */
  2846. /* it settles for the media sub-directory of the Windows directory.         */
  2847. // =======================================================================
  2848. void GetMediaStartPath( void )
  2849.     {
  2850.     HKEY    hReg;
  2851.     DWORD   cbStartPathLen;
  2852.  
  2853.     if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2854.             gszRegKeyDirect3D,
  2855.             0, KEY_READ, &hReg ))
  2856.     {
  2857.         goto REG_OPEN_FAILED;
  2858.     }
  2859.     else
  2860.     {
  2861.     // Query the Registry for the path to the media directory
  2862.     cbStartPathLen = sizeof( gszCDStartPath );
  2863.     if( ERROR_SUCCESS != RegQueryValueEx( hReg, gszRegValueD3DPath,
  2864.                 NULL, NULL,
  2865.                 gszCDStartPath, &cbStartPathLen ))
  2866.         {
  2867.         goto REG_OPEN_FAILED;
  2868.         }
  2869.     RegCloseKey( hReg );
  2870.     hReg = NULL;
  2871.     }
  2872.  
  2873.     return;
  2874.  
  2875. REG_OPEN_FAILED:
  2876.     // Start off by getting the Windows directory -- we're trying to build a
  2877.     // file path like "C:\WINDOWS\MEDIA", but the WINDOWS directory could be
  2878.     // named anything, so we must ask.
  2879.     GetWindowsDirectory( gszCDStartPath, sizeof(gszCDStartPath));
  2880.     // If there's no trailing backslash, append one
  2881.     if( lstrcmp( &gszCDStartPath[lstrlen(gszCDStartPath)], TEXT("\\") ))
  2882.     lstrcat( gszCDStartPath, TEXT("\\"));
  2883.     // Now add on the MEDIA part of the path
  2884.     lstrcat( gszCDStartPath, TEXT("MEDIA"));
  2885.     }
  2886.  
  2887.  
  2888. // =======================================================================
  2889. // fGetToken()
  2890. //
  2891. //    Parses the command-line string "in place" starting at pszStart.  A ptr
  2892. // to the start of the next token and it's length will be the out parameters,
  2893. // or NULL and 0 if no token.  Note that *ppszRet will NOT be NULL-terminated
  2894. // since the string is part of another string.  That's what then length is for.
  2895. //
  2896. // Returns: TRUE if a token was retrieved, or FALSE if there was no token.
  2897. // =======================================================================
  2898. BOOL fGetToken( PSTR pszStart, PSTR *ppszRet, int *pcchRet )
  2899.     {
  2900.     PSTR  pszCur = pszStart;
  2901.     PSTR  pszTokStart;
  2902.  
  2903.     if( !pszStart || NULL == ppszRet || NULL == pcchRet )
  2904.     return FALSE;
  2905.  
  2906.     // Skip leading whitespace
  2907.     while( *pszCur && (*pszCur == ' ' || *pszCur == '\t'))
  2908.     pszCur++;
  2909.  
  2910.     *ppszRet = NULL;
  2911.     *pcchRet = 0;
  2912.  
  2913.     if( *pszCur )
  2914.     {
  2915.     pszTokStart = pszCur;
  2916.  
  2917.     while( *pszCur && *pszCur != ' ' && *pszCur != '\t' )
  2918.         pszCur++;
  2919.  
  2920.     *ppszRet = pszTokStart;
  2921.     *pcchRet = (int)(pszCur - pszTokStart);
  2922.     }
  2923.  
  2924.     if( *pcchRet != 0 )
  2925.     return TRUE;
  2926.     else
  2927.     return FALSE;
  2928.     }
  2929.  
  2930.  
  2931. // =======================================================================
  2932. // fMatchToken()
  2933. //
  2934. //    Attempts to match the first cchLen characters of pszDatum to the
  2935. // string at pszString.  The comparison is case-insensitive (this function
  2936. // is designed for command-line switch matching).
  2937. //
  2938. // Returns: TRUE if the first cchLen characters are a match, else FALSE.
  2939. // =======================================================================
  2940. BOOL fMatchToken( PSTR pszString, PSTR pszDatum, int cchLen )
  2941.     {
  2942.     int    i;
  2943.  
  2944.     for( i = 0; i < cchLen; i++ )
  2945.     {
  2946.     if( CharLower( (LPTSTR)MAKELONG( pszString[i], 0 ))
  2947.             != CharLower( (LPTSTR)MAKELONG( pszDatum[i], 0 )))
  2948.         return FALSE;
  2949.     }
  2950.     return TRUE;
  2951.     }
  2952.  
  2953.  
  2954. // =======================================================================
  2955. // ParseCommandLine()
  2956. //
  2957. //    Given a command-line string without the module name, this function will
  2958. // parse the command line and takes action on whatever it finds there.
  2959. //
  2960. // Returns: TRUE if successful, or FALSE if there was an error.
  2961. // =======================================================================
  2962. BOOL ParseCommandLine(LPSTR lpszCmdLine)
  2963.     {
  2964.     PSTR    pszCur,pszToken;
  2965.     PSTR    ppszFiles[MAXCONTROLS];
  2966.     BOOL    fStartPlaying = FALSE, fStartLooping = FALSE;
  2967.     int        cchTokLen = 0, i, nFilesFound;
  2968.  
  2969.     pszCur = lpszCmdLine;
  2970.  
  2971.     // First get all the command line switches
  2972.     while( fGetToken(pszCur, &pszToken, &cchTokLen) &&
  2973.        (pszToken[0] == '/' || pszToken[0] == '-' ))
  2974.     {
  2975.     pszCur = pszToken + cchTokLen;
  2976.     pszToken++;
  2977.  
  2978.     if( fMatchToken( pszToken, "PLAY", 4 ))
  2979.         {
  2980.         fStartPlaying = TRUE;
  2981.         }
  2982.     else if( fMatchToken( pszToken, "LOOP", 4 ))
  2983.         {
  2984.         fStartLooping = TRUE;
  2985.         }
  2986.     else
  2987.         {
  2988.         // We don't recognize this mysterious switch, so eat it and move on
  2989.         }
  2990.     }
  2991.  
  2992.     // Anything left on the command-line will be treated as a filename and
  2993.     // we'll attempt to open it after we've found them all
  2994.     nFilesFound = 0;
  2995.     while( fGetToken(pszCur, &pszToken, &cchTokLen) && nFilesFound < MAXCONTROLS )
  2996.     {
  2997.     pszCur = pszToken + cchTokLen;
  2998.     ppszFiles[nFilesFound] = GlobalAllocPtr( GPTR, (cchTokLen+1)*sizeof(char));
  2999.     // Copy the token out of the command-line string and into our buffer
  3000.     CopyMemory( ppszFiles[nFilesFound], pszToken, cchTokLen*sizeof(char));
  3001.     // Append a NULL terminator to what we just copied (to be safe)
  3002.     *(ppszFiles[nFilesFound] + cchTokLen) = 0;
  3003.     nFilesFound++;
  3004.     }
  3005.     // This function will take the array of strings we've created and open
  3006.     // each string as a file.  It will obey the global fStartPlaying and
  3007.     // fStartLooping flags we may have already set above
  3008.     if( nFilesFound )
  3009.     BatchOpenFiles( ppszFiles, nFilesFound, fStartPlaying, fStartLooping );
  3010.  
  3011.     // Free the space we allocated
  3012.     for( i = 0; i < nFilesFound; i++ )
  3013.     {
  3014.     GlobalFreePtr( ppszFiles[i] );
  3015.     ppszFiles[i] = NULL;
  3016.     }
  3017.  
  3018.     // Returning TRUE means the caller should continue doing what they
  3019.     // were doing: we succeeded.
  3020.     return TRUE;
  3021.     }
  3022.  
  3023.  
  3024. // =======================================================================
  3025. // BatchOpenFiles()
  3026. //
  3027. //    Takes an array of string pointers and tries to open each as a file to
  3028. // playback.  If fPlay is TRUE, the files will be played as they are being
  3029. // opened.  If fLoop is TRUE, they will also be set to loop.
  3030. //
  3031. // Returns: FALSE in the event of catastrophic failure, otherwise TRUE.
  3032. // =======================================================================
  3033. BOOL BatchOpenFiles( PSTR *ppszFiles, int nFiles, BOOL fPlay, BOOL fLoop )
  3034.     {
  3035.     int i;
  3036.     FILEINFO *pfi;
  3037.     DWORD cSamples;
  3038.  
  3039.     // Cap the number of files we can load out of the given set if we'd load
  3040.     // too many otherwise
  3041.     if( GetNumControls(&FileInfoFirst) + nFiles > MAXCONTROLS )
  3042.     nFiles = MAXCONTROLS - GetNumControls(&FileInfoFirst);
  3043.     
  3044.     for( i = 0; i < nFiles; i++ )
  3045.     {
  3046.     if(( pfi = GlobalAllocPtr(GPTR, sizeof(FILEINFO))) == NULL )
  3047.         goto BOF_Fail;
  3048.  
  3049.     ZeroMemory( pfi, sizeof(FILEINFO));
  3050.     strcpy( pfi->szFileName, ppszFiles[i] );
  3051.  
  3052.     if( WaveLoadFile( ppszFiles[i], &pfi->cbSize, &cSamples,
  3053.               &pfi->pwfx, &pfi->pbData ) != 0 )
  3054.         goto BOF_LoopError;
  3055.  
  3056.     GetNextControlCoords( &FileInfoFirst, &pfi->cox, &pfi->coy );
  3057.     if( NewDirectSoundBuffer(pfi) != 0)
  3058.         goto BOF_LoopError;
  3059.     Assert( pfi->pbData != NULL );
  3060.  
  3061.     if( AddToList( &FileInfoFirst, pfi ) != 0 )
  3062.         goto BOF_LoopError;
  3063.  
  3064.     pfi->nFileName = 0;
  3065.  
  3066.     if( CreateControl( hWndMain, pfi, pfi->pwfx->nSamplesPerSec,
  3067.                (MAXPAN_TB-MINPAN_TB)/2, MINVOL_TB ) != 0 )
  3068.         {
  3069.         ReleaseDirectSoundBuffer(pfi);
  3070.         RemoveFromList( pfi, &FileInfoFirst );
  3071.         // RemoveFromList will do all the cleanup
  3072.         pfi = NULL;
  3073.         goto BOF_LoopError;
  3074.         }
  3075.     ChangeOutputVol(pfi);
  3076.     ChangeOutputFreq(pfi);
  3077.     ChangeOutputPan(pfi);
  3078.  
  3079.     // LOOP is only obeyed if PLAY was also specified
  3080.     if( fPlay )
  3081.         {
  3082.         if( fLoop )
  3083.         {
  3084.         pfi->fLooped = TRUE;
  3085.         SendMessage( pfi->hWndLooped_BN, BM_SETCHECK, TRUE, 0L );
  3086.         }
  3087.         SendMessage( hWndMain, WM_COMMAND, 0, (LPARAM)pfi->hWndPlay_BN );
  3088.         }
  3089.  
  3090.     // Avoid the in-loop error cleanup by using a continue statement here
  3091.     // to jump back up to the top
  3092.     continue;
  3093.     
  3094.     // Cleanup code in case we fail to open a particular file -- we should
  3095.     // just ignore this one and continue because we might still be able to
  3096.     // open other files
  3097.     BOF_LoopError:
  3098.     if( NULL != pfi )
  3099.         {
  3100.         if( NULL != pfi->pwfx )
  3101.         {
  3102.         GlobalFreePtr(pfi->pwfx);
  3103.         pfi->pwfx = NULL;
  3104.         }
  3105.         if( NULL != pfi->pbData )
  3106.         {
  3107.         GlobalFreePtr(pfi->pbData);
  3108.         pfi->pbData = NULL;
  3109.         }
  3110.  
  3111.         ReleaseDirectSoundBuffer(pfi);
  3112.         GlobalFreePtr(pfi);
  3113.         pfi = NULL;
  3114.         }
  3115.     }
  3116.  
  3117.     UpdateMainStatus();
  3118.  
  3119.     return TRUE;
  3120.  
  3121. BOF_Fail:
  3122.     return FALSE;
  3123.     }
  3124.  
  3125.  
  3126.  
  3127.  
  3128.