home *** CD-ROM | disk | FTP | other *** search
/ NEXT Generation 27 / NEXT27.iso / pc / demos / emperor / dx3.exe / SDK / SAMPLES / DSSHOW / SHELL.C < prev    next >
C/C++ Source or Header  |  1996-08-28  |  79KB  |  3,128 lines

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