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

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:   mstream.c
  6.  *  Content:   Illustrates streaming data from a disk MIDI file to a
  7.  *             MIDI stream buffer for playback.
  8.  *
  9.  ***************************************************************************/
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. #include <commctrl.h>
  13. #include <memory.h>
  14. #include <mmreg.h>
  15.  
  16. #include "resource.h"
  17. #include "debug.h"
  18. #include "midstuff.h"
  19. #include "mstream.h"
  20.  
  21. //////////////////////////////////////////////////////////////////////////////
  22. // Lots of global variables
  23.  
  24. char szAppClass[] = "MStreamWndClass";
  25. char szAppName[]  = "MStream";
  26.  
  27. char szAppTitle[64];
  28. char szAppCaption[64];
  29. char szOpenFilter[128];
  30. char szOpenDLGTitle[64];
  31. char szProgress[64];
  32. char szTempo[64];
  33. char szVolume[64];
  34.  
  35. char szTemp[256];
  36. char szDebug[256];
  37. char szFileBuffer[MAX_PATH];
  38. char szFileTitle[MAX_PATH];
  39.  
  40. HWND    hWndMain, hWndProgText, hWndProg, hWndVolText, hWndVol, hWndTempoText;
  41. HWND    hWndTempo, hWndLoopCheck, hWndPlay, hWndPause, hWndStop;
  42.  
  43. HINSTANCE   hInst;
  44.  
  45. BOOL    bFileOpen = FALSE, bPlaying = FALSE, bBuffersPrepared = FALSE;
  46. BOOL    bPaused = FALSE, bLooped = FALSE;
  47. UINT    uMIDIDeviceID = MIDI_MAPPER, uCallbackStatus;
  48. int nTextControlHeight, nCurrentBuffer, nEmptyBuffers;
  49. DWORD   dwBufferTickLength, dwTempoMultiplier, dwCurrentTempo, dwProgressBytes;
  50. DWORD   dwVolumePercent, dwVolCache[NUM_CHANNELS];
  51.  
  52. HMIDISTRM    hStream;
  53. CONVERTINFO  ciStreamBuffers[NUM_STREAM_BUFFERS];
  54.  
  55. // Private to this module...
  56. static HANDLE   hBufferReturnEvent;
  57.  
  58. // From another module...
  59. extern INFILESTATE  ifs;
  60.  
  61. ///////////////////////////////////////////////////////////////////////////////
  62. // Module-scope function declarations
  63.  
  64. static BOOL InitApp( HINSTANCE );
  65. static BOOL InitInstance( HINSTANCE, int );
  66. static void FreeBuffers( void );
  67.  
  68. /******************************************************************************
  69.  * WinMain()
  70.  *
  71.  * Entry point for all Windows programs - performs initialization, message loop
  72.  */
  73. int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  74.                     LPSTR lpCmdLine, int nCmdShow )
  75.     {
  76.     MSG     msg;
  77.  
  78.     hInst = hInstance;
  79.  
  80.     /* Make sure the common controls are loaded for our use */
  81.     InitCommonControls();
  82.  
  83. // Turn debugging output on or off
  84. #ifdef DEBUG
  85.     DbgInitialize( TRUE );
  86. #else
  87.     DbgInitialize( FALSE );
  88. #endif
  89.  
  90.     if( !hPrevInstance )
  91.         if( !InitApp( hInstance ))
  92.             {
  93.             ErrorMessageBox( IDS_ERROR_APPINIT, MB_ICONSTOP );
  94.             return( FALSE );
  95.         }
  96.  
  97.     if( !InitInstance( hInstance, nCmdShow ))
  98.         {
  99.         ErrorMessageBox( IDS_ERROR_INSTANCEINIT, MB_ICONSTOP );
  100.         return( FALSE );
  101.     }
  102.  
  103.     while( GetMessage((LPMSG)&msg, NULL, 0, 0 ))
  104.         {
  105.         TranslateMessage( &msg );
  106.         DispatchMessage( &msg );
  107.         }
  108.  
  109.     UnregisterClass( szAppClass, hInstance );
  110.     return( msg.wParam );
  111.     } /* End of WinMain() */
  112.  
  113.  
  114. /*****************************************************************************/
  115. /* InitApp()                                     */
  116. /*                                       */
  117. /*   Inits things that only need to be created once for the this application */
  118. /* (like creating the window class).                         */
  119. /*****************************************************************************/
  120. static BOOL InitApp( HINSTANCE hInstance )
  121.     {
  122.     WNDCLASS    wc;
  123.  
  124.     /* Set up and register a window class */
  125.     wc.style        = CS_HREDRAW | CS_VREDRAW;
  126.     wc.lpszClassName    = szAppClass;
  127.     wc.lpfnWndProc  = (WNDPROC)MainWindowProc;
  128.     wc.cbClsExtra   = 0;
  129.     wc.cbWndExtra   = sizeof( DWORD );
  130.     wc.hInstance    = hInstance;
  131.     wc.hIcon        = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON3 ));
  132.     wc.hCursor      = LoadCursor( NULL, IDC_ARROW );
  133.     wc.hbrBackground    = (HBRUSH)( COLOR_WINDOW );
  134.     wc.lpszMenuName = MAKEINTRESOURCE( IDR_MAINMENU );
  135.  
  136.     if( !RegisterClass( &wc ))
  137.         {
  138.     ErrorMessageBox( IDS_ERROR_REGISTERCLASS, MB_ICONSTOP );
  139.         return( FALSE );
  140.         }
  141.     return( TRUE );
  142.     } /* End of InitApp() */
  143.  
  144.  
  145. /*****************************************************************************/
  146. /* InitInstance()                                */
  147. /*                                       */
  148. /* Performs initialization that must be done for each application instance.  */
  149. /*                                       */
  150. /*****************************************************************************/
  151. static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
  152.     {
  153.     HWND    hWnd;
  154.     RECT    crect;
  155.     UINT    uCharsRead;
  156.     MMRESULT    mmrRetVal;
  157.  
  158.     LoadString( hInstance, IDS_APP_TITLE, szAppTitle, sizeof(szAppTitle));
  159.     LoadString( hInstance, IDS_APP_CAPTION, szAppCaption, sizeof(szAppCaption));
  160.     LoadString( hInstance, IDS_TBTITLE_VOLUME, szVolume, sizeof(szVolume));
  161.     LoadString( hInstance, IDS_TBTITLE_TEMPO, szTempo, sizeof(szTempo));
  162.     LoadString( hInstance, IDS_TBTITLE_PROGRESS,
  163.                             szProgress, sizeof(szProgress));
  164.     LoadString( hInstance, IDS_OPEN_DLGTITLE,
  165.                         szOpenDLGTitle, sizeof(szOpenDLGTitle));
  166. /* This is a little trick designed to allow us to load a common dialog box
  167.  * filter string, which is really a concatentation of several NULL-terminated
  168.  * strings. Note that while is is possible to enter something else into the
  169.  * resource as placeholders for the NULL characters, this has the undesireable
  170.  * effect of forcing us to search-and-replace byte-by-byte and doesn't make it
  171.  * as easy to internationalize our strings...
  172.  */
  173.     memset( szOpenFilter, 0, sizeof(szOpenFilter));
  174.     uCharsRead = LoadString( hInstance, IDS_OPEN_FILTER1,
  175.                 szOpenFilter, sizeof(szOpenFilter)) + 1;
  176.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER2,
  177.                     &szOpenFilter[uCharsRead],
  178.                     sizeof(szOpenFilter) - uCharsRead ) + 1;
  179.     uCharsRead += LoadString( hInstance, IDS_OPEN_FILTER3,
  180.                     &szOpenFilter[uCharsRead],
  181.                     sizeof(szOpenFilter) - uCharsRead ) + 1;
  182.     LoadString( hInstance, IDS_OPEN_FILTER4,
  183.                     &szOpenFilter[uCharsRead],
  184.                     sizeof(szOpenFilter) - uCharsRead );
  185.  
  186.     /* Create an application window */
  187.     hWnd = CreateWindow( szAppClass,        /* class name */
  188.             szAppCaption,       /* caption for window */
  189.             WS_OVERLAPPEDWINDOW,    /* style */
  190.             CW_USEDEFAULT,      /* x position */
  191.             CW_USEDEFAULT,      /* y position */
  192.             CW_USEDEFAULT,      /* width */
  193.             CW_USEDEFAULT,      /* height */
  194.             NULL,           /* parent window */
  195.             NULL,           /* menu */
  196.             hInstance,      /* instance */
  197.             NULL );         /* parms */
  198.  
  199.     if( !hWnd )
  200.         {
  201.     ErrorMessageBox( IDS_ERROR_MAINWNDCREATE, MB_ICONSTOP );
  202.         return( FALSE );
  203.     }
  204.  
  205.     hWndMain = hWnd;
  206.     GetClientRect( hWndMain, &crect );
  207.  
  208.     /* Create some controls for things like volume, tempo, progress, etc. */
  209.     if( CreateChildren( crect ))
  210.         return( FALSE );
  211.  
  212.     // Resize window, now that we know the height of the static text controls
  213.     SetWindowPos( hWnd, NULL, 0, 0,
  214.                 2 * BORDER_SPACE_CX + TEMPO_TB_CX + 2 * CONTROL_SPACE_CX
  215.                 + VOL_TB_CX + CHECK_CX,
  216.             2 * BORDER_SPACE_CY + nTextControlHeight + TEXT_SPACE_CY
  217.             + CONTROL_SPACE_CY + BUTTON_CY + TEMPO_TB_CY,
  218.             SWP_NOMOVE | SWP_NOZORDER );
  219.  
  220.     ShowWindow( hWnd, nCmdShow );
  221.     UpdateWindow( hWnd );
  222.  
  223.     if(( mmrRetVal = midiStreamOpen( &hStream,
  224.                     &uMIDIDeviceID,
  225.                     (DWORD)1, (DWORD)MidiProc,
  226.                     (DWORD)0,
  227.                     CALLBACK_FUNCTION )) != MMSYSERR_NOERROR )
  228.     {
  229.     MidiErrorMessageBox( mmrRetVal );
  230.     return( FALSE );
  231.     }
  232.  
  233.     return( TRUE );
  234.     } /* End of InitInstance() */
  235.  
  236.  
  237. /****************************************************************************/
  238. /* MainWindowProc()                                                         */
  239. /*                                                                          */
  240. /*    Messages for our main window are handled here                         */
  241. /*                                                                          */
  242. /****************************************************************************/
  243. LRESULT CALLBACK MainWindowProc( HWND hWnd, unsigned uMsg,
  244.                         WPARAM wParam, LPARAM lParam )
  245.     {
  246.     LPMINMAXINFO    lpMinMax;
  247.     DWORD   dwCDErr = 0;
  248.     BOOL    bResult = FALSE;
  249.     MMRESULT    mmrRetVal;
  250.     
  251.     switch( uMsg )
  252.         {
  253.     case WM_CREATE:
  254.         hBufferReturnEvent = CreateEvent( NULL, FALSE,
  255.                         FALSE, "Wait For Buffer Return" );
  256.         break;
  257.  
  258.     case WM_MSTREAM_UPDATEVOLUME:
  259.         SetChannelVolume( wParam, dwVolumePercent );
  260.         break;
  261.  
  262.     case WM_MSTREAM_PROGRESS:
  263.         /* Set the Progress text */
  264.             wsprintf( szTemp, "%s: %lu bytes", szProgress, dwProgressBytes );
  265.             Static_SetText( hWndProgText, szTemp );
  266.         break;
  267.  
  268.         case WM_COMMAND:
  269.             switch( LOWORD( wParam ))
  270.                 {
  271.                 case IDM_FILE_OPEN:
  272.                     {
  273.                     OPENFILENAME    ofn;
  274.     /*
  275.      * Clear out and fill in an OPENFILENAME structure in preparation
  276.      * for creating a common dialog box to open a file.
  277.      */
  278.                     memset( &ofn, 0, sizeof(OPENFILENAME));
  279.                     ofn.lStructSize = sizeof(OPENFILENAME);
  280.                     ofn.hwndOwner   = hWnd;
  281.                     ofn.hInstance   = hInst;
  282.                     ofn.lpstrFilter = szOpenFilter;
  283.                     ofn.nFilterIndex    = 1;
  284.                     szFileBuffer[0] = '\0';
  285.                     ofn.lpstrFile   = szFileBuffer;
  286.                     ofn.nMaxFile    = sizeof(szFileBuffer);
  287.                     ofn.lpstrFileTitle  = szFileTitle;
  288.                     ofn.nMaxFileTitle   = sizeof(szFileTitle);
  289.                     ofn.lpstrDefExt = "MID";
  290.                     ofn.lpstrTitle  = szOpenDLGTitle;
  291.                     ofn.Flags       = OFN_FILEMUSTEXIST;
  292.  
  293.                     bResult = GetOpenFileName( &ofn ); /* Do the dialog box */
  294.  
  295.     /*
  296.      *  A return of TRUE indicates that the user did not select a filename.
  297.      * The possible reasons are: Cancel was clicked, or an error occured.
  298.      * If Cancel was clicked, the CommDlgExtendedError() function will not
  299.      * return a valid code.  For anything else, an error code will come back.
  300.      */
  301.                     if( bResult == FALSE )
  302.                         {
  303.                         dwCDErr = CommDlgExtendedError();
  304.                         if( dwCDErr )
  305.                             {
  306.                             /* Handle a common dialog box error */
  307.                             HandleCommDlgError( dwCDErr );
  308.                             }
  309.                         else    /* Clicked Cancel, so finish msg processing */
  310.                             return( 0 );
  311.                         }
  312.                     else
  313.                         {
  314.                         if( bFileOpen )
  315.                             {
  316. // Need to close the previous file before we open a new one.  The best way to
  317. // do this is by faking a menu command, so that we only have the actual code in
  318. // one place and it can easily be changed.
  319.                             SendMessage( hWnd, WM_COMMAND,
  320.                                     MAKEWPARAM( IDM_FILE_CLOSE, 0 ), 0L );
  321.                             }
  322.                     
  323.                         if( StreamBufferSetup())
  324.                             {
  325.                             // Error opening the MIDI file so abort
  326.                 // The function already took care of notification
  327.                 break;
  328.                             }
  329.                         else
  330.                             {
  331.                             bFileOpen = TRUE;
  332.                             EnableWindow( hWndPlay, TRUE );
  333.                             BuildTitleBarText();
  334.                             }
  335.                         }
  336.                     }
  337.                     break;
  338.  
  339.                 case IDM_FILE_CLOSE:
  340.             SendMessage( hWnd, WM_COMMAND,
  341.                     MAKEWPARAM( IDC_STOP,
  342.                     MSTREAM_STOPF_NOREOPEN ), 0L );
  343.                     BuildTitleBarText();
  344.                     break;
  345.  
  346.                 case IDM_HELP_ABOUT:
  347.                     DialogBox( hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndMain,
  348.                             (DLGPROC)DLG_About );
  349.                     break;
  350.  
  351.         case IDM_ACTIONS_PAUSE:
  352.                 case IDC_PAUSE:
  353.             if( bPaused )
  354.             midiStreamRestart( hStream );
  355.             else
  356.             midiStreamPause( hStream );
  357.             bPaused = !bPaused;
  358.                     // If we're paused, the title bar will show (Paused)
  359.                     BuildTitleBarText();
  360.             break;
  361.  
  362.         case IDM_ACTIONS_LOOPED:
  363.         case IDC_LOOPCHECK:
  364.             Button_SetCheck( hWndLoopCheck, !bLooped );
  365.             bLooped = !bLooped;
  366.             break;
  367.  
  368.         case IDM_ACTIONS_PLAY:
  369.                 case IDC_PLAY:
  370.             // Clicking play while playback is paused will un-pause it
  371.             if( bPaused )
  372.             {
  373.             SendMessage( hWnd, WM_COMMAND,
  374.                     MAKEWPARAM( IDC_PAUSE, 0 ), 0L );
  375.             break;
  376.             }
  377.                     // Clicking play while playing will restart from scratch
  378.                     if( bPlaying )
  379.                 {
  380.                 // Stop the file, allowing it to bve reset so that we
  381.             // can start it over again from the beginning
  382.                 SendMessage( hWnd, WM_COMMAND,
  383.                         MAKEWPARAM( IDC_STOP, 0 ), 0L );
  384.             }
  385.  
  386.                     if( bFileOpen )
  387.                         {
  388.                         // Clear the status of our callback so it will handle
  389.             // MOM_DONE callbacks once more
  390.                         uCallbackStatus = 0;
  391.                         if(( mmrRetVal = midiStreamRestart( hStream ))
  392.                                         != MMSYSERR_NOERROR )
  393.                 {
  394.                 MidiErrorMessageBox( mmrRetVal );
  395.                 break;
  396.                 }
  397.                         }
  398.                     else
  399.                         {
  400.                         bPlaying = FALSE;
  401.                         break;
  402.                         }
  403.  
  404.             bPlaying = TRUE;
  405.             EnableWindow( hWndPause, TRUE );
  406.             EnableWindow( hWndStop, TRUE );
  407.                     break;
  408.  
  409.                 case IDM_ACTIONS_STOP:
  410.                 case IDC_STOP:
  411.                     if( bFileOpen || bPlaying
  412.                             || ( uCallbackStatus != STATUS_CALLBACKDEAD ))
  413.             {
  414.             EnableWindow( hWndStop, FALSE );
  415.             EnableWindow( hWndPause, FALSE );
  416.             bPlaying = bPaused = FALSE;
  417.             if( uCallbackStatus != STATUS_CALLBACKDEAD && uCallbackStatus != STATUS_WAITINGFOREND )
  418.                 uCallbackStatus = STATUS_KILLCALLBACK;
  419.  
  420.                         if(( mmrRetVal = midiStreamStop( hStream ))
  421.                                         != MMSYSERR_NOERROR )
  422.                 {
  423.                 MidiErrorMessageBox( mmrRetVal );
  424.                 break;
  425.                 }
  426.                         if(( mmrRetVal = midiOutReset( (HMIDIOUT)hStream ))
  427.                                         != MMSYSERR_NOERROR )
  428.                 {
  429.                 MidiErrorMessageBox( mmrRetVal );
  430.                 break;
  431.                 }
  432. // Wait for the callback thread to release this thread, which it will do by
  433. // calling SetEvent() once all buffers are returned to it
  434.                         if( WaitForSingleObject( hBufferReturnEvent,
  435.                                     DEBUG_CALLBACK_TIMEOUT )
  436.                                             == WAIT_TIMEOUT )
  437.                 {
  438. // Note, this is a risky move because the callback may be genuinely busy, but
  439. // when we're debugging, it's safer and faster than freezing the application,
  440. // which leaves the MIDI device locked up and forces a system reset...
  441.                 DebugPrint( "Timed out waiting for MIDI callback" );
  442.                 uCallbackStatus = STATUS_CALLBACKDEAD;
  443.                 }
  444.                         }
  445.  
  446.             if( uCallbackStatus == STATUS_CALLBACKDEAD )
  447.             {
  448.             uCallbackStatus = 0;
  449.             if( bFileOpen )
  450.                 {
  451.                 ConverterCleanup();
  452.                 FreeBuffers();
  453.                 if( hStream )
  454.                 {
  455.                 if(( mmrRetVal = midiStreamClose( hStream ))
  456.                                 != MMSYSERR_NOERROR )
  457.                     {
  458.                     MidiErrorMessageBox( mmrRetVal );
  459.                     }
  460.                 hStream = NULL;
  461.                 }
  462.  
  463.                 EnableWindow( hWndPlay, FALSE );
  464.                 bFileOpen = FALSE;
  465.                 }
  466.                         
  467.                         if(!( HIWORD(wParam) & MSTREAM_STOPF_NOREOPEN ))
  468.                             {
  469.                             if( StreamBufferSetup())
  470.                 {
  471.                 // Error setting up for MIDI file
  472.                 // Notification is already taken care of...
  473.                 break;
  474.                 }
  475.                             else
  476.                                 {
  477.                                 bFileOpen = TRUE;
  478.                                 EnableWindow( hWndPlay, TRUE );
  479.                                 }
  480.                             }
  481.             BuildTitleBarText();    // Update the title bar
  482.                         }
  483.                     break;
  484.  
  485.                 case IDM_FILE_EXIT:
  486.                     DestroyWindow( hWnd );
  487.                     break;
  488.                 }
  489.             break;
  490.  
  491.     case WM_INITMENU:
  492.         if( bFileOpen )
  493.         {
  494.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_PLAY,
  495.                                             MF_BYCOMMAND | MF_ENABLED );
  496.         EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  497.                                             MF_BYCOMMAND | MF_ENABLED );
  498.         }
  499.         else
  500.         {
  501.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_PLAY,
  502.                                             MF_BYCOMMAND | MF_GRAYED );
  503.         EnableMenuItem( GetMenu( hWnd ), IDM_FILE_CLOSE,
  504.                                             MF_BYCOMMAND | MF_GRAYED );
  505.         }
  506.         if( bLooped )
  507.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_LOOPED,
  508.                                             MF_BYCOMMAND | MF_ENABLED );
  509.         else
  510.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_LOOPED,
  511.                                             MF_BYCOMMAND | MF_GRAYED );
  512.         if( bPlaying )
  513.         {
  514.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_PAUSE,
  515.                                             MF_BYCOMMAND | MF_ENABLED );
  516.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_STOP,
  517.                                             MF_BYCOMMAND | MF_ENABLED );
  518.         }
  519.         else
  520.         {
  521.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_PAUSE,
  522.                                             MF_BYCOMMAND | MF_GRAYED );
  523.         EnableMenuItem( GetMenu( hWnd ), IDM_ACTIONS_STOP,
  524.                                             MF_BYCOMMAND | MF_GRAYED );
  525.         }
  526.         break;
  527.  
  528.         case WM_GETMINMAXINFO:
  529.     /*
  530.      * We know exactly how big this window should be, and it's sort of a
  531.      * little pop-up control panel, so we can disable window sizing by
  532.      * forcing all the minimum and maximum sizes to be the calculated size.
  533.      */
  534.             lpMinMax = (LPMINMAXINFO)lParam;
  535.  
  536.             lpMinMax->ptMaxSize.x = 2*CONTROL_SPACE_CX + 2*BORDER_SPACE_CX
  537.                                     + CHECK_CX + TEMPO_TB_CX + VOL_TB_CX
  538.                                     + 2*GetSystemMetrics( SM_CXBORDER );
  539.             lpMinMax->ptMaxSize.y = 2*(BORDER_SPACE_CY
  540.                                     + GetSystemMetrics( SM_CYBORDER ))
  541.                                     + TEXT_SPACE_CY + nTextControlHeight
  542.                                     + TEMPO_TB_CY + BUTTON_CY
  543.                                     + CONTROL_SPACE_CY
  544.                                     + GetSystemMetrics( SM_CYMENU )
  545.                                     + GetSystemMetrics( SM_CYCAPTION );
  546.  
  547.             lpMinMax->ptMinTrackSize.x = lpMinMax->ptMaxTrackSize.x
  548.                                         = lpMinMax->ptMaxSize.x;
  549.  
  550.             lpMinMax->ptMinTrackSize.y = lpMinMax->ptMaxTrackSize.y
  551.                                         = lpMinMax->ptMaxSize.y;
  552.             break;
  553.  
  554.         case WM_HSCROLL:
  555.             if(((HWND)lParam == hWndTempo) && bFileOpen )
  556.                 {
  557.                 HandleTempoScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  558.                 }
  559.             else if(((HWND)lParam == hWndVol) && bFileOpen )
  560.                 {
  561.                 HandleVolScroll( (int)LOWORD(wParam), (int)HIWORD(wParam));
  562.                 }
  563.             break;
  564.  
  565.     case WM_ENDSESSION:
  566.         // If the sesson is ending, we need to do our WM_DESTROY processing
  567.         if (!wParam) break;
  568.         // NOTE!!! we are falling through to the WM_CLOSE processing.
  569.         case WM_DESTROY:
  570.             // Stop anything that might be playing and send a flag which will
  571.         // tell the code not to automatically reload the file for replay.
  572.             if( hStream )
  573.         SendMessage( hWnd, WM_COMMAND,
  574.                           MAKEWPARAM( IDC_STOP, MSTREAM_STOPF_NOREOPEN ), 0 );
  575.  
  576.         FreeBuffers();
  577.  
  578.         if( hStream )
  579.         {
  580.         if(( mmrRetVal = midiStreamClose( hStream ))
  581.                                 != MMSYSERR_NOERROR )
  582.             {
  583.             MidiErrorMessageBox( mmrRetVal );
  584.             }
  585.         hStream = NULL;
  586.         }
  587.  
  588.             CloseHandle( hBufferReturnEvent );
  589.             
  590.             PostQuitMessage( 0 );
  591.             break;
  592.  
  593.         default:
  594.             return DefWindowProc( hWnd, uMsg, wParam, lParam );
  595.         }
  596.     return 0L;
  597.     } /* MainWindowProc */
  598.  
  599.  
  600. /*****************************************************************************/
  601. /* DLG_About()                                                               */
  602. /*                                                                           */
  603. /*   Dialog procedure for the Help...About... box which simply pops up a     */
  604. /* little copyright message and brief program description.                   */
  605. /*                                                                           */
  606. /*****************************************************************************/
  607. BOOL CALLBACK DLG_About( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  608.     {
  609.     switch( msg )
  610.         {
  611.         case WM_INITDIALOG:
  612.             break;
  613.  
  614.         case WM_COMMAND:
  615.             switch( LOWORD(wParam))
  616.                 {
  617.                 case IDOK:
  618.                     EndDialog( hDlg, FALSE );
  619.                     return( TRUE );
  620.  
  621.                 default:
  622.                     break;
  623.                 }
  624.             break;
  625.  
  626.         default:
  627.             return( FALSE );
  628.         }
  629.  
  630.     return( FALSE );
  631.     }
  632.  
  633.  
  634. /*****************************************************************************/
  635. /* FreeBuffers()                                                             */
  636. /*                                                                           */
  637. /*   This function unprepares and frees all our buffers -- something we must */
  638. /* do to work around a bug in MMYSYSTEM that prevents a device from playing  */
  639. /* back properly unless it is closed and reopened after each stop.           */
  640. /*****************************************************************************/
  641. void FreeBuffers( void )
  642.     {
  643.     DWORD   idx;
  644.     MMRESULT    mmrRetVal;
  645.  
  646.     if( bBuffersPrepared )
  647.     {
  648.     for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
  649.         if(( mmrRetVal = midiOutUnprepareHeader( (HMIDIOUT)hStream,
  650.                 &ciStreamBuffers[idx].mhBuffer,
  651.                 sizeof(MIDIHDR)))
  652.                         != MMSYSERR_NOERROR )
  653.         {
  654.         MidiErrorMessageBox( mmrRetVal );
  655.         }
  656.         bBuffersPrepared = FALSE;
  657.         }
  658.     // Free our stream buffers...
  659.     for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
  660.         if( ciStreamBuffers[idx].mhBuffer.lpData )
  661.         {
  662.         GlobalFreePtr( ciStreamBuffers[idx].mhBuffer.lpData );
  663.         ciStreamBuffers[idx].mhBuffer.lpData = NULL;
  664.         }
  665.     }
  666.  
  667.  
  668. /*****************************************************************************/
  669. /* StreamBufferSetup()                                                       */
  670. /*                                                                           */
  671. /*    This function uses the filename stored in the global character array to*/
  672. /* open a MIDI file. Then it goes about converting at least the first part of*/
  673. /* that file into a midiStream buffer for playback.                          */
  674. /*****************************************************************************/
  675. BOOL StreamBufferSetup( void )
  676.     {
  677.     int     nChkErr;
  678.     BOOL    bFoundEnd = FALSE;
  679.     DWORD   dwConvertFlag, idx;
  680.  
  681.     MMRESULT        mmrRetVal;
  682.     MIDIPROPTIMEDIV mptd;
  683.  
  684.     if( !hStream )
  685.     if(( mmrRetVal = midiStreamOpen( &hStream,
  686.                     &uMIDIDeviceID,
  687.                     (DWORD)1, (DWORD)MidiProc,
  688.                     (DWORD)0,
  689.                     CALLBACK_FUNCTION )) != MMSYSERR_NOERROR )
  690.         {
  691.         MidiErrorMessageBox( mmrRetVal );
  692.         return( TRUE );
  693.         }
  694.  
  695.     for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
  696.     {
  697.     ciStreamBuffers[idx].mhBuffer.dwBufferLength = OUT_BUFFER_SIZE;
  698.     if(( ciStreamBuffers[idx].mhBuffer.lpData
  699.         = GlobalAllocPtr( GHND, OUT_BUFFER_SIZE )) == NULL )
  700.         {
  701.         // Buffers we already allocated will be killed by WM_DESTROY
  702.         // after we fail on the create by returning with -1
  703.         return( -1 );
  704.         }
  705.     }
  706.     if( ConverterInit( szFileBuffer ))
  707.     return( TRUE );
  708.  
  709.     // Initialize the volume cache array to some pre-defined value
  710.     for( idx = 0; idx < NUM_CHANNELS; idx++ )
  711.     dwVolCache[idx] = VOL_CACHE_INIT;
  712.  
  713.     mptd.cbStruct = sizeof(mptd);
  714.     mptd.dwTimeDiv = ifs.dwTimeDivision;
  715.     if(( mmrRetVal = midiStreamProperty( hStream, (LPBYTE)&mptd,
  716.                         MIDIPROP_SET | MIDIPROP_TIMEDIV ))
  717.                                 != MMSYSERR_NOERROR )
  718.     {
  719.     MidiErrorMessageBox( mmrRetVal );
  720.     ConverterCleanup();
  721.     return( TRUE );
  722.     }
  723.  
  724.     nEmptyBuffers = 0;
  725.     dwConvertFlag = CONVERTF_RESET;
  726.  
  727.     for( nCurrentBuffer = 0; nCurrentBuffer < NUM_STREAM_BUFFERS;
  728.                                     nCurrentBuffer++ )
  729.     {
  730.     // Tell the converter to convert up to one entire buffer's length of output
  731.     // data. Also, set a flag so it knows to reset any saved state variables it
  732.     // may keep from call to call.
  733.     ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
  734.     ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
  735.     ciStreamBuffers[nCurrentBuffer].tkStart = 0;
  736.         ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
  737.  
  738.     if(( nChkErr = ConvertToBuffer( dwConvertFlag,
  739.                     &ciStreamBuffers[nCurrentBuffer] ))
  740.                                 != CONVERTERR_NOERROR )
  741.             {
  742.         if( nChkErr == CONVERTERR_DONE )
  743.         {
  744.         bFoundEnd = TRUE;
  745.         }
  746.         else
  747.         {
  748.         DebugPrint( "Initial conversion pass failed" );
  749.         ConverterCleanup();
  750.         return( TRUE );
  751.         }
  752.         }
  753.     ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
  754.                 = ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
  755.  
  756.     if( !bBuffersPrepared )
  757.         if(( mmrRetVal = midiOutPrepareHeader( (HMIDIOUT)hStream,
  758.                     &ciStreamBuffers[nCurrentBuffer].mhBuffer,
  759.                     sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
  760.         {
  761.         MidiErrorMessageBox( mmrRetVal );
  762.         ConverterCleanup();
  763.         return( TRUE );
  764.         }
  765.     if(( mmrRetVal = midiStreamOut( hStream,
  766.                     &ciStreamBuffers[nCurrentBuffer].mhBuffer,
  767.                     sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
  768.         {
  769.         MidiErrorMessageBox( mmrRetVal );
  770.         break;
  771.         }
  772.     dwConvertFlag = 0;
  773.  
  774.     if( bFoundEnd )
  775.         break;
  776.     }
  777.  
  778.     bBuffersPrepared = TRUE;
  779.     nCurrentBuffer = 0;
  780.     UpdateFromControls();
  781.     return( FALSE );
  782.     }
  783.  
  784.  
  785. /*****************************************************************************/
  786. /* MidiProc()                                                                */
  787. /*                                                                           */
  788. /*   This is the callback handler which continually refills MIDI data buffers*/
  789. /* as they're returned to us from the audio subsystem.                       */
  790. /*****************************************************************************/
  791. void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD dwInstance,
  792.             DWORD dwParam1, DWORD dwParam2 )
  793.     {
  794.     static int  nWaitingBuffers = 0;
  795.     MIDIEVENT   *pme;
  796.     MIDIHDR *pmh;
  797.  
  798.     MMRESULT    mmrRetVal;
  799.     int     nChkErr;
  800.  
  801.  
  802.     switch( uMsg )
  803.     {
  804.     case MOM_DONE:
  805.             if( uCallbackStatus == STATUS_CALLBACKDEAD )
  806.                 return;
  807.  
  808.         nEmptyBuffers++;
  809.  
  810.         if( uCallbackStatus == STATUS_WAITINGFOREND )
  811.         {
  812.         if( nEmptyBuffers < NUM_STREAM_BUFFERS )
  813.             {
  814.             return;
  815.             }
  816.         else
  817.             {
  818.             uCallbackStatus = STATUS_CALLBACKDEAD;
  819.             PostMessage( hWndMain, WM_COMMAND,
  820.                     MAKEWPARAM( IDC_STOP, 0 ), 0L );
  821.             SetEvent( hBufferReturnEvent );
  822.             return;
  823.             }
  824.         }
  825.  
  826.     // This flag is set whenever the callback is waiting for all buffers to
  827.     // come back.
  828.             if( uCallbackStatus == STATUS_KILLCALLBACK )
  829.         {
  830.         // Count NUM_STREAM_BUFFERS-1 being returned for the last time
  831.         if( nEmptyBuffers < NUM_STREAM_BUFFERS )
  832.                     {
  833.                     return;
  834.                     }
  835.         // Then send a stop message when we get the last buffer back...
  836.         else
  837.             {
  838.             // Change the status to callback dead
  839.             uCallbackStatus = STATUS_CALLBACKDEAD;
  840.             SetEvent( hBufferReturnEvent );
  841.             return;
  842.             }
  843.                 }
  844.  
  845.             dwProgressBytes
  846.                 += ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded;
  847.         PostMessage( hWndMain, WM_MSTREAM_PROGRESS, 0L, 0L );
  848.  
  849. ///////////////////////////////////////////////////////////////////////////////
  850. // Fill an available buffer with audio data again...
  851.  
  852.         if( bPlaying && nEmptyBuffers )
  853.         {
  854.         ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
  855.         ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
  856.         ciStreamBuffers[nCurrentBuffer].tkStart = 0;
  857.         ciStreamBuffers[nCurrentBuffer].dwBytesRecorded = 0;
  858.         ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
  859.  
  860.         if(( nChkErr = ConvertToBuffer( 0,
  861.                         &ciStreamBuffers[nCurrentBuffer] ))
  862.                                 != CONVERTERR_NOERROR )
  863.                 {
  864.             if( nChkErr == CONVERTERR_DONE )
  865.             {
  866.             // Don't include this one in the count
  867.             nWaitingBuffers = NUM_STREAM_BUFFERS - 1;
  868.             uCallbackStatus = STATUS_WAITINGFOREND;
  869.             return;
  870.             }
  871.             else
  872.             {
  873.             DebugPrint( "MidiProc() conversion pass failed!" );
  874.             ConverterCleanup();
  875.             return;
  876.             }
  877.             }
  878.  
  879.         ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
  880.                 = ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
  881.  
  882.         if(( mmrRetVal = midiStreamOut( hStream,
  883.                     &ciStreamBuffers[nCurrentBuffer].mhBuffer,
  884.                     sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
  885.             {
  886.             MidiErrorMessageBox( mmrRetVal );
  887.             ConverterCleanup();
  888.             return;
  889.             }
  890.         nCurrentBuffer = ( nCurrentBuffer + 1 ) % NUM_STREAM_BUFFERS;
  891.         nEmptyBuffers--;
  892.         }
  893.  
  894.         break;
  895.  
  896.     case MOM_POSITIONCB:
  897.         pmh = (MIDIHDR *)dwParam1;
  898.         pme = (MIDIEVENT *)(pmh->lpData + pmh->dwOffset);
  899.         if( MIDIEVENT_TYPE( pme->dwEvent ) == MIDI_CTRLCHANGE )
  900.         {
  901.         if( MIDIEVENT_DATA1( pme->dwEvent ) == MIDICTRL_VOLUME_LSB )
  902.             {
  903.             DebugPrint( "Got an LSB volume event" );
  904.             break;
  905.             }
  906.         if( MIDIEVENT_DATA1( pme->dwEvent ) != MIDICTRL_VOLUME )
  907.             break;
  908.  
  909.         // Mask off the channel number and cache the volume data byte
  910.         dwVolCache[ MIDIEVENT_CHANNEL( pme->dwEvent )]
  911.                     = MIDIEVENT_VOLUME( pme->dwEvent );
  912.         // Post a message so that the main program knows to counteract
  913.         // the effects of the volume event in the stream with its own
  914.         // generated event which reflects the proper trackbar position.
  915.         PostMessage( hWndMain, WM_MSTREAM_UPDATEVOLUME,
  916.                 MIDIEVENT_CHANNEL( pme->dwEvent ), 0L );
  917.         }
  918.         break;
  919.  
  920.     default:
  921.         break;
  922.     }
  923.  
  924.     return;
  925.     }
  926.