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

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:   mstrconv.c
  6.  *  Content:    Converts a MIDI file into a midiStream, placing the results
  7.  *              in a buffer that the MSTREAM sample application can use for
  8.  *              playback with the midiStream* API under Win95.
  9.  *
  10.  ***************************************************************************/
  11. #include <windows.h>
  12. #include <windowsx.h>
  13.  
  14. #include <mmsystem.h>
  15. #include <assert.h>
  16. #include <stdio.h>
  17.  
  18. #include "debug.h"
  19. #include "mstream.h"
  20. #include "midstuff.h"
  21.  
  22. // Global stuff which is defined in the main module
  23. //
  24. extern char szTemp[256];
  25. extern char szDebug[256];
  26. extern char szAppTitle[64];
  27.  
  28. BOOL    bInsertTempo = FALSE;
  29.  
  30. // A few global variables used by this module only
  31. //
  32. static HANDLE   hInFile = INVALID_HANDLE_VALUE;
  33. INFILESTATE ifs;
  34. static DWORD    tkCurrentTime;
  35.  
  36. // Tracks how many malloc blocks exist. If there are any and we decide to shut
  37. // down, we must scan for them and free them.  Malloc blocks are only created as
  38. // temporary storgae blocks for extra parameter data associated with MIDI_SYSEX,
  39. // MIDI_SYSEXEND, and MIDI_META events.
  40. static DWORD    dwMallocBlocks  = 0;
  41.  
  42. extern DWORD    dwBufferTickLength, dwTempoMultiplier, dwCurrentTempo;
  43. extern DWORD    dwProgressBytes, dwVolumePercent;
  44. extern BOOL bLooped;
  45.  
  46. // Messages
  47. //
  48. static char szInitErrMem[]  = "Out of memory.\n";
  49. static char szInitErrInFile[]   = "Read error on input file or file is corrupt.\n";
  50. static char szNoTrackBuffMem[]  = "Insufficient memory for track buffer allocation\n";
  51.  
  52. #ifdef DEBUG
  53. static char gteBadRunStat[]     = "Reference to missing running status.";
  54. static char gteRunStatMsgTrunc[]= "Running status message truncated";
  55. static char gteChanMsgTrunc[]   = "Channel message truncated";
  56. static char gteSysExLenTrunc[]  = "SysEx event truncated (length)";
  57. static char gteSysExTrunc[] = "SysEx event truncated";
  58. static char gteMetaNoClass[]    = "Meta event truncated (no class byte)";
  59. static char gteMetaLenTrunc[]   = "Meta event truncated (length)";
  60. static char gteMetaTrunc[]  = "Meta event truncated";
  61. static char gteNoMem[]      = "Out of memory during malloc call";
  62. #endif
  63.  
  64. // Prototypes
  65. //
  66. static int  AddEventToStreamBuffer( PTEMPEVENT pteTemp, LPCONVERTINFO );
  67. static BOOL GetInFileData( LPVOID lpDest, DWORD cbToGet );
  68. static BOOL GetTrackByte( PINTRACKSTATE ptsTrack, LPBYTE lpbyByte );
  69. static BOOL GetTrackEvent( PINTRACKSTATE ptsTrack, PTEMPEVENT pteTemp );
  70. static BOOL GetTrackVDWord( PINTRACKSTATE ptsTrack, LPDWORD lpdw );
  71. static BOOL RefillTrackBuffer( PINTRACKSTATE ptsTrack );
  72. static BOOL RewindConverter( void );
  73.  
  74. #ifdef DEBUG
  75. static void     ShowTrackError( PINTRACKSTATE ptsTrack, char* szErr );
  76. #endif
  77.  
  78. // ConverterInit
  79. // 
  80. // Open the input file
  81. // Allocate and read the entire input file into memory
  82. // Validate the input file structure
  83. // Allocate the input track structures and initialize them
  84. // Initialize the output track structures
  85. //
  86. // Return TRUE on success
  87. // Prints its own error message if something goes wrong
  88. //
  89. BOOL ConverterInit( LPSTR szInFile )
  90.     {
  91.     BOOL        fRet = TRUE;
  92.     DWORD       cbRead, dwTag, cbHeader, dwToRead;
  93.     MIDIFILEHDR     Header;
  94.     PINTRACKSTATE   ptsTrack;
  95.     UINT        idx;
  96.  
  97.     tkCurrentTime = 0;
  98.  
  99.     // Initialize things we'll try to free later if we fail
  100.     //
  101.     memset( &ifs, 0, sizeof(INFILESTATE));
  102.     ifs.cbFileLength = 0;
  103.     ifs.pitsTracks = NULL;
  104.  
  105.     // Attempt to open the input and output files
  106.     //
  107.     hInFile = CreateFile( szInFile, GENERIC_READ,
  108.             FILE_SHARE_READ, NULL, OPEN_EXISTING,
  109.             FILE_ATTRIBUTE_NORMAL, NULL);
  110.     if( hInFile == INVALID_HANDLE_VALUE )
  111.     {
  112.     wsprintf( szTemp, "Could not open \"%s\" for read.\n", szInFile );
  113.     MessageBox( GetActiveWindow(), szTemp,
  114.             szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  115.     goto Init_Cleanup;
  116.     }
  117.  
  118. // Figure out how big the input file is.
  119.     if((( ifs.cbFileLength = GetFileSize( hInFile, NULL )) == (UINT)-1 ))
  120.     {
  121.     MessageBox( GetActiveWindow(), "File system error on input file.\n",
  122.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  123.     goto Init_Cleanup;
  124.     }
  125.  
  126. // Set up to read from the memory buffer. Read and validate
  127. // - MThd header
  128. // - size of file header chunk
  129. // - file header itself
  130. //  
  131.     if( GetInFileData( &dwTag, sizeof(DWORD))
  132.         || ( dwTag != MThd )
  133.         || GetInFileData( &cbHeader, sizeof(DWORD))
  134.         || (( cbHeader = DWORDSWAP( cbHeader )) < sizeof(MIDIFILEHDR))
  135.             || GetInFileData( &Header, cbHeader ) )
  136.         {
  137.         MessageBox( GetActiveWindow(), szInitErrInFile,
  138.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  139.         goto Init_Cleanup;
  140.         }
  141.  
  142. // File header is stored in hi-lo order. Swap this into Intel order and save
  143. // parameters in our native int size (32 bits)
  144. //
  145.     ifs.dwFormat = (DWORD)WORDSWAP( Header.wFormat );
  146.     ifs.dwTrackCount = (DWORD)WORDSWAP( Header.wTrackCount );
  147.     ifs.dwTimeDivision = (DWORD)WORDSWAP( Header.wTimeDivision );
  148.  
  149. // We know how many tracks there are; allocate the structures for them and parse
  150. // them. The parse merely looks at the MTrk signature and track chunk length
  151. // in order to skip to the next track header.
  152. //
  153.     ifs.pitsTracks = (PINTRACKSTATE)GlobalAllocPtr( GPTR,
  154.                     ifs.dwTrackCount * sizeof(INTRACKSTATE));
  155.     if( ifs.pitsTracks == NULL )
  156.         {
  157.         MessageBox( GetActiveWindow(), szInitErrMem,
  158.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  159.         goto Init_Cleanup;
  160.         }
  161.  
  162.     for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
  163.                                 ++idx, ++ptsTrack )
  164.     {
  165.     if(( ptsTrack->pTrackStart
  166.             = GlobalAllocPtr( GHND, TRACK_BUFFER_SIZE )) == NULL )
  167.         {
  168.         MessageBox( GetActiveWindow(), szNoTrackBuffMem,
  169.                     szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  170.         goto Init_Cleanup;
  171.         }
  172.  
  173.     if( GetInFileData( &dwTag, sizeof(dwTag)) || ( dwTag != MTrk )
  174.             || GetInFileData( &cbHeader, sizeof(cbHeader)))
  175.         {
  176.         MessageBox( GetActiveWindow(), szInitErrInFile,
  177.                     szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  178.         goto Init_Cleanup;
  179.         }
  180.  
  181.     cbHeader = DWORDSWAP( cbHeader );
  182.     ptsTrack->dwTrackLength = cbHeader; // Total track length
  183. ///////////////////////////////////////////////////////////////////////////////
  184. // Here we need to determine if all track data will fit into a single one of
  185. // our track buffers.  If not, we need to read in a buffer full and come back
  186. // for more later, saving the file offset to continue from and the amount left
  187. // to read in the track structure.
  188.  
  189.     // Save the file offset of the beginning of this track
  190.     ptsTrack->foTrackStart = SetFilePointer( hInFile, 0, NULL,
  191.                         FILE_CURRENT );
  192.  
  193.     if( ptsTrack->dwTrackLength > TRACK_BUFFER_SIZE )
  194.         dwToRead = TRACK_BUFFER_SIZE;
  195.     else
  196.         dwToRead = ptsTrack->dwTrackLength;
  197.     if( !ReadFile( hInFile, ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
  198.         || ( cbRead != dwToRead ))
  199.         {
  200.         MessageBox( GetActiveWindow(), szInitErrInFile,
  201.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  202.         goto Init_Cleanup;
  203.         }
  204.     // Save the number of bytes that didn't make it into the buffer
  205.     ptsTrack->dwLeftOnDisk = ptsTrack->dwTrackLength - cbRead;
  206.     ptsTrack->dwLeftInBuffer = cbRead;
  207.     // Save the current file offset so we can seek to it later
  208.     ptsTrack->foNextReadStart = SetFilePointer( hInFile, 0,
  209.                             NULL, FILE_CURRENT );
  210.  
  211.         // Setup pointer to the current position in the track
  212.         ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
  213.         ptsTrack->fdwTrack = 0;
  214.         ptsTrack->byRunningStatus = 0;
  215.         ptsTrack->tkNextEventDue = 0;
  216.  
  217.         // Handle bozo MIDI files which contain empty track chunks
  218.         //
  219.         if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
  220.             {
  221.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  222.             continue;
  223.             }
  224.  
  225.         // We always preread the time from each track so the mixer code can
  226.         // determine which track has the next event with a minimum of work
  227.         //
  228.         if( GetTrackVDWord( ptsTrack, &ptsTrack->tkNextEventDue ))
  229.             {
  230.             MessageBox( GetActiveWindow(), szInitErrInFile,
  231.                     szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  232.             goto Init_Cleanup;
  233.             }
  234.     // Step over any unread data, advancing to the beginning of the next
  235.     // track's data
  236.     SetFilePointer( hInFile, ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
  237.             NULL, FILE_BEGIN );
  238.         }   // End of track initialization code
  239.  
  240.     fRet = FALSE;
  241.     
  242.     Init_Cleanup:
  243.     if( fRet )
  244.         ConverterCleanup();
  245.  
  246.     return( fRet );
  247.     }
  248.  
  249. //
  250. // GetInFileData
  251. //
  252. // Gets the requested number of bytes of data from the input file and returns
  253. // a pointer to them.
  254. // 
  255. // Returns a pointer to the data or NULL if we'd read more than is
  256. // there.
  257. //
  258. static BOOL GetInFileData( LPVOID lpDest, DWORD cbToGet )
  259.     {
  260.     DWORD   cbRead;
  261.  
  262.     if( !ReadFile( hInFile, lpDest, cbToGet, &cbRead, NULL )
  263.             || ( cbRead != cbToGet ))
  264.         {
  265.         return( TRUE );
  266.         }
  267.  
  268.     return( FALSE );
  269.     }
  270.  
  271.  
  272. //
  273. // ConverterCleanup
  274. //
  275. // Free anything we ever allocated
  276. //
  277. void ConverterCleanup( void )
  278.     {
  279.     DWORD   idx;
  280.  
  281.     if( hInFile != INVALID_HANDLE_VALUE )
  282.         {
  283.         CloseHandle( hInFile );
  284.     hInFile = INVALID_HANDLE_VALUE;
  285.     }
  286.  
  287.     if( ifs.pitsTracks )
  288.         {
  289.     // De-allocate all our track buffers
  290.     for( idx = 0; idx < ifs.dwTrackCount; idx++ )
  291.         if( ifs.pitsTracks[idx].pTrackStart )
  292.         GlobalFreePtr( ifs.pitsTracks[idx].pTrackStart );
  293.  
  294.         GlobalFreePtr( ifs.pitsTracks );
  295.     ifs.pitsTracks = NULL;
  296.     }
  297.     }
  298.  
  299.  
  300. /*****************************************************************************/
  301. /* RewindConverter()                                                         */
  302. /*                                                                           */
  303. /*   This little function is an adaptation of the ConverterInit() code which */
  304. /* resets the tracks without closing and opening the file, thus reducing the */
  305. /* time it takes to loop back to the beginning when looping.                 */
  306. /*****************************************************************************/
  307. static BOOL RewindConverter( void )
  308.     {
  309.     DWORD   dwToRead, cbRead, idx;
  310.     BOOL    fRet;
  311.  
  312.     PINTRACKSTATE   ptsTrack;
  313.  
  314.     tkCurrentTime = 0;
  315.  
  316.     for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
  317.                                 ++idx, ++ptsTrack )
  318.     {
  319. ///////////////////////////////////////////////////////////////////////////////
  320. // Here we need to determine if all track data will fit into a single one of
  321. // our track buffers.  If not, we need to read in a buffer full and come back
  322. // for more later, saving the file offset to continue from and the amount left
  323. // to read in the track structure.
  324.  
  325.     SetFilePointer( hInFile, ptsTrack->foTrackStart, NULL, FILE_BEGIN );
  326.  
  327.     if( ptsTrack->dwTrackLength > TRACK_BUFFER_SIZE )
  328.         dwToRead = TRACK_BUFFER_SIZE;
  329.     else
  330.         dwToRead = ptsTrack->dwTrackLength;
  331.     if( !ReadFile( hInFile, ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
  332.         || ( cbRead != dwToRead ))
  333.         {
  334.         MessageBox( GetActiveWindow(), szInitErrInFile,
  335.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  336.         goto Rewind_Cleanup;
  337.         }
  338.     // Save the number of bytes that didn't make it into the buffer
  339.     ptsTrack->dwLeftOnDisk = ptsTrack->dwTrackLength - cbRead;
  340.     ptsTrack->dwLeftInBuffer = cbRead;
  341.     // Save the current file offset so we can seek to it later
  342.     ptsTrack->foNextReadStart = SetFilePointer( hInFile, 0,
  343.                             NULL, FILE_CURRENT );
  344.  
  345.         // Setup pointer to the current position in the track
  346.         ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
  347.         ptsTrack->fdwTrack = 0;
  348.         ptsTrack->byRunningStatus = 0;
  349.         ptsTrack->tkNextEventDue = 0;
  350.  
  351.  
  352.         // Handle bozo MIDI files which contain empty track chunks
  353.         //
  354.         if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
  355.             {
  356.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  357.             continue;
  358.             }
  359.  
  360.         // We always preread the time from each track so the mixer code can
  361.         // determine which track has the next event with a minimum of work
  362.         //
  363.         if( GetTrackVDWord( ptsTrack, &ptsTrack->tkNextEventDue ))
  364.             {
  365.             MessageBox( GetActiveWindow(), szInitErrInFile,
  366.                     szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  367.             goto Rewind_Cleanup;
  368.             }
  369.     // Step over any unread data, advancing to the beginning of the next
  370.     // track's data
  371.     SetFilePointer( hInFile, ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
  372.             NULL, FILE_BEGIN );
  373.         }   // End of track initialization code
  374.  
  375.     fRet = FALSE;
  376.  
  377.     Rewind_Cleanup:
  378.  
  379.     if( fRet )
  380.     return( TRUE );
  381.  
  382.     return( FALSE );
  383.     }
  384.  
  385.  
  386. /*****************************************************************************/
  387. /* ConvertToBuffer()                                                         */
  388. /*                                                                           */
  389. /*    This function converts MIDI data from the track buffers setup by a     */
  390. /* previous call to ConverterInit().  It will convert data until an error is */
  391. /* encountered or the output buffer has been filled with as much event data  */
  392. /* as possible, not to exceed dwMaxLength. This function can take a couple   */
  393. /* bit flags, passed through dwFlags. Information about the success/failure  */
  394. /* of this operation and the number of output bytes actually converted will  */
  395. /* be returned in the CONVERTINFO structure pointed at by lpciInfo.          */
  396. /*                                                                           */
  397. /*****************************************************************************/
  398. int ConvertToBuffer( DWORD dwFlags, LPCONVERTINFO lpciInfo )
  399.     {
  400.     static INTRACKSTATE *ptsTrack, *ptsFound;
  401.     static DWORD    dwStatus;
  402.     static DWORD    tkNext;
  403.     static TEMPEVENT    teTemp;
  404.  
  405.     int     nChkErr;
  406.     DWORD   idx;
  407.  
  408.     lpciInfo->dwBytesRecorded = 0;
  409.  
  410.     if( dwFlags & CONVERTF_RESET )
  411.     {
  412.     dwProgressBytes = 0;
  413.     dwStatus = 0;
  414.     memset( &teTemp, 0, sizeof(TEMPEVENT));
  415.     ptsTrack = ptsFound = NULL;
  416.     }
  417.     // If we were already done, then return with a warning...
  418.     if( dwStatus & CONVERTF_STATUS_DONE )
  419.     {
  420.     if( bLooped )
  421.         {
  422.         RewindConverter();
  423.         dwProgressBytes = 0;
  424.         dwStatus = 0;
  425.         }
  426.     else
  427.         return( CONVERTERR_DONE );
  428.     }
  429.     // The caller is asking us to continue, but we're already hosed because we
  430.     // previously identified something as corrupt, so complain louder this time.
  431.     else if( dwStatus & CONVERTF_STATUS_STUCK )
  432.         {
  433.     return( CONVERTERR_STUCK );
  434.     }
  435.     else if( dwStatus & CONVERTF_STATUS_GOTEVENT )
  436.         {
  437.     // Turn off this bit flag
  438.     dwStatus ^= CONVERTF_STATUS_GOTEVENT;
  439.  
  440.     /*
  441.      *  The following code for this case is duplicated from below, and is
  442.      * designed to handle a "straggler" event, should we have one left over
  443.      * from previous processing the last time this function was called.
  444.      */
  445.  
  446.     // Don't add end of track event 'til we're done
  447.     //
  448.     if( teTemp.byShortData[0] == MIDI_META
  449.                     && teTemp.byShortData[1] == MIDI_META_EOT )
  450.         {
  451.         if( dwMallocBlocks )
  452.         {
  453.         free( teTemp.pLongData );
  454.         dwMallocBlocks--;
  455.         }
  456.         }
  457.  
  458.     else if(( nChkErr = AddEventToStreamBuffer( &teTemp, lpciInfo ))
  459.                             != CONVERTERR_NOERROR )
  460.         {
  461.         if( nChkErr == CONVERTERR_BUFFERFULL )
  462.         {
  463.         // Do some processing and tell caller that this buffer's full
  464.         dwStatus |= CONVERTF_STATUS_GOTEVENT;
  465.         return( CONVERTERR_NOERROR );
  466.         }
  467.         else if( nChkErr == CONVERTERR_METASKIP )
  468.         {
  469.         // We skip by all meta events that aren't tempo changes...
  470.         }
  471.         else
  472.         {
  473.         DebugPrint( "Unable to add event to stream buffer." );
  474.         if( dwMallocBlocks )
  475.             {
  476.             free( teTemp.pLongData );
  477.             dwMallocBlocks--;
  478.             }
  479.         return( TRUE );
  480.         }
  481.         }
  482.     }
  483.  
  484.     for( ; ; )
  485.         {
  486.         ptsFound = NULL;
  487.         tkNext = 0xFFFFFFFFL;
  488.         // Find nearest event due
  489.         //
  490.         for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
  491.                                     ++idx, ++ptsTrack )
  492.             {
  493.             if(( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ))
  494.             && ( ptsTrack->tkNextEventDue < tkNext ))
  495.                 {
  496.                 tkNext = ptsTrack->tkNextEventDue;
  497.                 ptsFound = ptsTrack;
  498.                 }
  499.         }
  500.  
  501.     // None found?  We must be done, so return to the caller with a smile.
  502.     //
  503.     if( !ptsFound )
  504.         {
  505.         dwStatus |= CONVERTF_STATUS_DONE;
  506.             // Need to set return buffer members properly
  507.         return( CONVERTERR_NOERROR );
  508.         }
  509.  
  510.     // Ok, get the event header from that track
  511.     //
  512.     if( GetTrackEvent( ptsFound, &teTemp ))
  513.             {
  514.         // Warn future calls that this converter is stuck at a corrupt spot
  515.         // and can't continue
  516.             dwStatus |= CONVERTF_STATUS_STUCK;
  517.             return( CONVERTERR_CORRUPT );
  518.             }
  519.  
  520.     // Don't add end of track event 'til we're done
  521.     //
  522.     if( teTemp.byShortData[0] == MIDI_META
  523.                     && teTemp.byShortData[1] == MIDI_META_EOT )
  524.         {
  525.         if( dwMallocBlocks )
  526.         {
  527.         free( teTemp.pLongData );
  528.         dwMallocBlocks--;
  529.         }
  530.         continue;
  531.         }
  532.  
  533.     if(( nChkErr = AddEventToStreamBuffer( &teTemp, lpciInfo ))
  534.                             != CONVERTERR_NOERROR )
  535.         {
  536.         if( nChkErr == CONVERTERR_BUFFERFULL )
  537.         {
  538.         // Do some processing and tell somebody this buffer is full...
  539.         dwStatus |= CONVERTF_STATUS_GOTEVENT;
  540.         return( CONVERTERR_NOERROR );
  541.         }
  542.         else if( nChkErr == CONVERTERR_METASKIP )
  543.         {
  544.         // We skip by all meta events that aren't tempo changes...
  545.         }
  546.         else
  547.         {
  548.         DebugPrint( "Unable to add event to stream buffer." );
  549.         if( dwMallocBlocks )
  550.             {
  551.             free( teTemp.pLongData );
  552.             dwMallocBlocks--;
  553.             }
  554.         return( TRUE );
  555.         }
  556.         }
  557.         }   
  558.  
  559.     return( CONVERTERR_NOERROR );
  560.     }
  561.  
  562.  
  563. //
  564. // GetTrackVDWord
  565. //
  566. // Attempts to parse a variable length DWORD from the given track. A VDWord
  567. // in a MIDI file
  568. //  (a) is in lo-hi format 
  569. //  (b) has the high bit set on every byte except the last
  570. //
  571. // Returns the DWORD in *lpdw and TRUE on success; else
  572. // FALSE if we hit end of track first. Sets ITS_F_ENDOFTRK
  573. // if we hit end of track.
  574. //
  575. static BOOL GetTrackVDWord( PINTRACKSTATE ptsTrack, LPDWORD lpdw )
  576.     {
  577.     BYTE    byByte;
  578.     DWORD   dw = 0;
  579.  
  580.     if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
  581.         return( TRUE );
  582.  
  583.     do
  584.         {
  585.         if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
  586.             {
  587.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  588.             return( TRUE );
  589.             }
  590.  
  591.         if( GetTrackByte( ptsTrack, &byByte ))
  592.         return( TRUE );
  593.  
  594.         dw = ( dw << 7 ) | ( byByte & 0x7F );
  595.         } while( byByte & 0x80 );
  596.  
  597.     *lpdw = dw;
  598.  
  599.     return( FALSE );
  600.     }
  601.  
  602.  
  603. //
  604. // GetTrackEvent
  605. //
  606. // Fills in the event struct with the next event from the track
  607. //
  608. // pteTemp->tkEvent will contain the absolute tick time of the event
  609. // pteTemp->byShortData[0] will contain
  610. //  MIDI_META if the event is a meta event;
  611. //   in this case pteTemp->byShortData[1] will contain the meta class
  612. //  MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event
  613. //  Otherwise, the event is a channel message and pteTemp->byShortData[1]
  614. //   and pteTemp->byShortData[2] will contain the rest of the event.
  615. //
  616. // pteTemp->dwEventLength will contain
  617. //  The total length of the channel message in pteTemp->byShortData if
  618. //   the event is a channel message
  619. //  The total length of the paramter data pointed to by
  620. //   pteTemp->pLongData otherwise
  621. //
  622. // pteTemp->pLongData will point at any additional paramters if the 
  623. //  event is a SysEx or meta event with non-zero length; else
  624. //  it will contain NULL
  625. //
  626. // Returns FALSE on success or TRUE on any kind of parse error
  627. // Prints its own error message ONLY in the debug version
  628. //
  629. // Maintains the state of the input track (i.e. ptsTrack->dwLeftInBuffer,
  630. // ptsTrack->pTrackPointers, and ptsTrack->byRunningStatus).
  631. //
  632. static BOOL GetTrackEvent( INTRACKSTATE *ptsTrack, PTEMPEVENT pteTemp )
  633.     {
  634.     DWORD   idx;
  635.     BYTE    byByte;
  636.     UINT    dwEventLength;
  637.  
  638.     // Clear out the temporary event structure to get rid of old data...
  639.     memset( pteTemp, 0, sizeof(TEMPEVENT));
  640.  
  641.     // Already at end of track? There's nothing to read.
  642.     //
  643.     if(( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
  644.             || ( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk ))
  645.         return( TRUE );
  646.  
  647.     // Get the first byte, which determines the type of event.
  648.     //
  649.     if( GetTrackByte( ptsTrack, &byByte ))
  650.     return( TRUE );
  651.  
  652.     // If the high bit is not set, then this is a channel message
  653.     // which uses the status byte from the last channel message
  654.     // we saw. NOTE: We do not clear running status across SysEx or
  655.     // meta events even though the spec says to because there are
  656.     // actually files out there which contain that sequence of data.
  657.     //
  658.     if( !( byByte & 0x80 ))
  659.         {
  660.         // No previous status byte? We're hosed.
  661.         if( !ptsTrack->byRunningStatus )
  662.             {
  663.             TRACKERR(ptsTrack, gteBadRunStat);
  664.             return( TRUE );
  665.             }
  666.  
  667.         pteTemp->byShortData[0] = ptsTrack->byRunningStatus;
  668.         pteTemp->byShortData[1] = byByte;
  669.  
  670.         byByte = pteTemp->byShortData[0] & 0xF0;
  671.         pteTemp->dwEventLength = 2;
  672.  
  673.         // Only program change and channel pressure events are 2 bytes long;
  674.         // the rest are 3 and need another byte
  675.         //
  676.         if(( byByte != MIDI_PRGMCHANGE ) && ( byByte != MIDI_CHANPRESS ))
  677.             {
  678.             if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
  679.                 {
  680.                 TRACKERR( ptsTrack, gteRunStatMsgTrunc );
  681.                 ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  682.                 return( TRUE );
  683.                 }
  684.  
  685.             if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
  686.         return( TRUE );
  687.             ++pteTemp->dwEventLength;
  688.             }
  689.         }
  690.     else if(( byByte & 0xF0 ) != MIDI_SYSEX )
  691.         {
  692.         // Not running status, not in SysEx range - must be
  693.         // normal channel message (0x80-0xEF)
  694.         //
  695.         pteTemp->byShortData[0] = byByte;
  696.         ptsTrack->byRunningStatus = byByte;
  697.         
  698.         // Strip off channel and just keep message type
  699.         //
  700.         byByte &= 0xF0;
  701.  
  702.         dwEventLength = ( byByte == MIDI_PRGMCHANGE || byByte == MIDI_CHANPRESS ) ? 1 : 2;
  703.         pteTemp->dwEventLength = dwEventLength + 1;
  704.  
  705.         if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk ) < dwEventLength )
  706.             {
  707.             TRACKERR( ptsTrack, gteChanMsgTrunc );
  708.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  709.             return( TRUE );
  710.             }
  711.  
  712.     if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
  713.         return( TRUE );
  714.         if( dwEventLength == 2 )
  715.             if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
  716.         return( TRUE );
  717.         } 
  718.     else if(( byByte == MIDI_SYSEX ) || ( byByte == MIDI_SYSEXEND ))
  719.         {
  720.         // One of the SysEx types. (They are the same as far as we're concerned;
  721.         // there is only a semantic difference in how the data would actually
  722.         // get sent when the file is played. We must take care to put the proper
  723.         // event type back on the output track, however.)
  724.         //
  725.         // Parse the general format of:
  726.         //  BYTE    bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
  727.         //  VDWORD  cbParms
  728.         //  BYTE    abParms[cbParms]
  729.         //
  730.         pteTemp->byShortData[0] = byByte;
  731.         if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength ))
  732.             {
  733.             TRACKERR( ptsTrack, gteSysExLenTrunc );
  734.             return( TRUE );
  735.             }
  736.  
  737.         if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk )
  738.                                 < pteTemp->dwEventLength )
  739.             {
  740.             TRACKERR( ptsTrack, gteSysExTrunc );
  741.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  742.             return( TRUE );
  743.             }
  744.  
  745.     // Malloc a temporary memory block to hold the parameter data
  746.     if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength )) == NULL )
  747.         {
  748.         TRACKERR( ptsTrack, gteNoMem );
  749.         return( TRUE );
  750.         }
  751.     // Copy from the input buffer to the parameter data buffer
  752.     for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
  753.         if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx ))
  754.         {
  755.         TRACKERR( ptsTrack, gteSysExTrunc );
  756.         return( TRUE );
  757.         }
  758.     // Increment our counter, which tells the program to look around for
  759.     // a malloc block to free, should it need to exit or reset before the
  760.     // block would normally be freed
  761.     dwMallocBlocks++;
  762.         } 
  763.     else if( byByte == MIDI_META )
  764.         {
  765.         // It's a meta event. Parse the general form:
  766.         //  BYTE    bEvent  (MIDI_META)
  767.         //  BYTE    bClass
  768.         //  VDWORD  cbParms
  769.         //  BYTE    abParms[cbParms]
  770.         //
  771.         pteTemp->byShortData[0] = byByte;
  772.  
  773.         if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
  774.             {
  775.             TRACKERR(ptsTrack, gteMetaNoClass );
  776.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  777.             return( TRUE );
  778.             }
  779.  
  780.     if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
  781.         return( TRUE );
  782.  
  783.         if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength ))
  784.             {   
  785.             TRACKERR( ptsTrack, gteMetaLenTrunc );
  786.             return( TRUE );
  787.             }
  788.  
  789.         // NOTE: It's perfectly valid to have a meta with no data
  790.         // In this case, dwEventLength == 0 and pLongData == NULL
  791.         //
  792.         if( pteTemp->dwEventLength )
  793.             {       
  794.             if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk )
  795.                                     < pteTemp->dwEventLength )
  796.                 {
  797.                 TRACKERR( ptsTrack, gteMetaTrunc );
  798.                 ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  799.                 return( TRUE );
  800.                 }
  801.  
  802.     // Malloc a temporary memory block to hold the parameter data
  803.         if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength ))
  804.                                         == NULL )
  805.         {
  806.         TRACKERR( ptsTrack, gteNoMem );
  807.         return( TRUE );
  808.         }
  809.     // Copy from the input buffer to the parameter data buffer
  810.         for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
  811.          if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx ))
  812.              {
  813.              TRACKERR( ptsTrack, gteMetaTrunc );
  814.              return( TRUE );
  815.              }
  816.     // Increment our counter, which tells the program to look around for
  817.     // a malloc block to free, should it need to exit or reset before the
  818.     // block would normally be freed
  819.         dwMallocBlocks++;
  820.             }
  821.  
  822.         if( pteTemp->byShortData[1] == MIDI_META_EOT )
  823.             ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
  824.         }
  825.     else
  826.         {
  827.         // Messages in this range are system messages and aren't supposed to
  828.         // be in a normal MIDI file. If they are, we've either misparsed or the
  829.         // authoring software is stupid.
  830.         //
  831.         return( TRUE );
  832.         }
  833.  
  834.     // Event time was already stored as the current track time
  835.     //
  836.     pteTemp->tkEvent = ptsTrack->tkNextEventDue;
  837.  
  838.     // Now update to the next event time. The code above MUST properly
  839.     // maintain the end of track flag in case the end of track meta is
  840.     // missing.  NOTE: This code is a continuation of the track event
  841.     // time pre-read which is done at the end of track initialization.
  842.     //
  843.     if( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ))
  844.         {
  845.         DWORD   tkDelta;
  846.  
  847.         if( GetTrackVDWord( ptsTrack, &tkDelta ))
  848.             return( TRUE );
  849.  
  850.         ptsTrack->tkNextEventDue += tkDelta;
  851.         }
  852.  
  853.     return( FALSE );
  854.     }
  855.  
  856.  
  857. //
  858. // GetTrackByte
  859. //
  860. // Retrieve the next byte from the track buffer, refilling the buffer from
  861. // disk if necessary.
  862. //
  863. static BOOL GetTrackByte( PINTRACKSTATE ptsTrack, LPBYTE lpbyByte )
  864.     {
  865.     if( !ptsTrack->dwLeftInBuffer )
  866.     {
  867.     if( RefillTrackBuffer( ptsTrack ))
  868.         return( TRUE );
  869.     }
  870.  
  871.     *lpbyByte = *ptsTrack->pTrackCurrent++;
  872.     ptsTrack->dwLeftInBuffer--;
  873.     return( FALSE );
  874.     }
  875.  
  876.  
  877. //
  878. // RefillTrackBuffer()
  879. //
  880. // This function attempts to read in a buffer-full of data for a MIDI track.
  881. //
  882. BOOL RefillTrackBuffer( PINTRACKSTATE ptsTrack )
  883.     {
  884.     DWORD   dwBytesRead, dwResult;
  885.     BOOL    bResult;
  886.  
  887.     if( ptsTrack->dwLeftOnDisk )
  888.         {
  889.         ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
  890.  
  891.         // Seek to the proper place in the file, indicated by
  892.         // ptsTrack->foNextReadStart and read in the remaining data,
  893.         // up to a maximum of the buffer size.
  894.  
  895.         if(( dwResult = SetFilePointer( hInFile,
  896.                         (long)ptsTrack->foNextReadStart,
  897.                         0L, FILE_BEGIN )) == 0xFFFFFFFF )
  898.             {
  899.             MessageBox( GetActiveWindow(),
  900.                 "Unable to seek to track buffer location in RefillTrackBuffer()!!",
  901.                 szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  902.             return( TRUE );
  903.             }
  904.         if( ptsTrack->dwLeftOnDisk > TRACK_BUFFER_SIZE )
  905.             ptsTrack->dwLeftInBuffer = TRACK_BUFFER_SIZE;
  906.         else
  907.             ptsTrack->dwLeftInBuffer = ptsTrack->dwLeftOnDisk;
  908.         bResult = ReadFile( hInFile, ptsTrack->pTrackStart,
  909.                 ptsTrack->dwLeftInBuffer,
  910.                 &dwBytesRead, NULL );
  911.  
  912.         ptsTrack->dwLeftOnDisk -= dwBytesRead;
  913.         ptsTrack->foNextReadStart = dwResult + dwBytesRead;
  914.     ptsTrack->dwLeftInBuffer = dwBytesRead;
  915.  
  916.         if( !bResult || ( bResult && !dwBytesRead )
  917.             || ( bResult && dwBytesRead != ptsTrack->dwLeftInBuffer ))
  918.             {
  919.             MessageBox( GetActiveWindow(),
  920.             "Read operation failed prematurely!!",
  921.             szAppTitle, MB_OK | MB_ICONEXCLAMATION );
  922.         ptsTrack->dwLeftInBuffer = dwBytesRead;
  923.             return( TRUE );
  924.             }
  925.         else
  926.         return( FALSE );
  927.         }
  928.  
  929.     return( TRUE );
  930.     }
  931.  
  932.  
  933. //
  934. // AddEventToStreamBuffer
  935. //
  936. // Put the given event into the given stream buffer at the given location
  937. // pteTemp must point to an event filled out in accordance with the
  938. // description given in GetTrackEvent
  939. //
  940. // Returns FALSE on sucess or TRUE on an error condition
  941. // Handles its own error notification by displaying to the appropriate
  942. // output device (either our debugging window, or the screen).
  943. //
  944. static int AddEventToStreamBuffer( PTEMPEVENT pteTemp, CONVERTINFO *lpciInfo )
  945.     {
  946.     DWORD   tkNow, tkDelta;
  947.     MIDIEVENT   *pmeEvent;
  948.  
  949.     pmeEvent = (MIDIEVENT *)( lpciInfo->mhBuffer.lpData
  950.                             + lpciInfo->dwStartOffset
  951.                             + lpciInfo->dwBytesRecorded );
  952.  
  953.     // When we see a new, empty buffer, set the start time on it...
  954.     if( !lpciInfo->dwBytesRecorded )
  955.     lpciInfo->tkStart = tkCurrentTime;
  956.  
  957.     // Use the above set start time to figure out how much longer we should fill
  958.     // this buffer before officially declaring it as "full"
  959.     if( tkCurrentTime - lpciInfo->tkStart > dwBufferTickLength )
  960.     if( lpciInfo->bTimesUp )
  961.         {
  962.         lpciInfo->bTimesUp = FALSE;
  963.         return( CONVERTERR_BUFFERFULL );
  964.         }
  965.     else
  966.         lpciInfo->bTimesUp = TRUE;
  967.  
  968.     tkNow = tkCurrentTime;
  969.  
  970.     // Delta time is absolute event time minus absolute time
  971.     // already gone by on this track
  972.     tkDelta = pteTemp->tkEvent - tkCurrentTime;
  973.  
  974.     // Event time is now current time on this track
  975.     tkCurrentTime = pteTemp->tkEvent;
  976.  
  977.     if( bInsertTempo )
  978.     {
  979.     bInsertTempo = FALSE;
  980.  
  981.     if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD))
  982.         {
  983.         // Cleanup from our write operation
  984.         return( CONVERTERR_BUFFERFULL );
  985.         }
  986.     if( dwCurrentTempo )
  987.         {
  988.         pmeEvent->dwDeltaTime = 0;
  989.         pmeEvent->dwStreamID = 0;
  990.         pmeEvent->dwEvent = ( dwCurrentTempo * 100 ) / dwTempoMultiplier;
  991.         pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
  992.     
  993.         lpciInfo->dwBytesRecorded += 3 * sizeof(DWORD);
  994.         pmeEvent += 3 * sizeof(DWORD);
  995.         }
  996.     }
  997.  
  998.     if( pteTemp->byShortData[0] < MIDI_SYSEX )
  999.         {
  1000.         // Channel message. We know how long it is, just copy it.
  1001.         // Need 3 DWORD's: delta-t, stream-ID, event
  1002.     if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD))
  1003.         {
  1004.         // Cleanup from our write operation
  1005.         return( CONVERTERR_BUFFERFULL );
  1006.         }
  1007.  
  1008.     pmeEvent->dwDeltaTime = tkDelta;
  1009.     pmeEvent->dwStreamID = 0;
  1010.     pmeEvent->dwEvent = ( pteTemp->byShortData[0] )
  1011.                 | (((DWORD)pteTemp->byShortData[1] ) << 8 )
  1012.                 | (((DWORD)pteTemp->byShortData[2] ) << 16 )
  1013.                 | MEVT_F_SHORT;
  1014.  
  1015.         if((( pteTemp->byShortData[0] & 0xF0) == MIDI_CTRLCHANGE )
  1016.             && ( pteTemp->byShortData[1] == MIDICTRL_VOLUME ))
  1017.         {
  1018.         // If this is a volume change, generate a callback so we can grab
  1019.         // the new volume for our cache
  1020.         pmeEvent->dwEvent |= MEVT_F_CALLBACK;
  1021.         }
  1022.     lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
  1023.     }
  1024.     else if(( pteTemp->byShortData[0] == MIDI_SYSEX )
  1025.         || ( pteTemp->byShortData[0] == MIDI_SYSEXEND ))
  1026.     {
  1027.     DebugPrint( "AddEventToStreamBuffer: Ignoring SysEx event." );
  1028.     if( dwMallocBlocks )
  1029.         {
  1030.         free( pteTemp->pLongData );
  1031.         dwMallocBlocks--;
  1032.         }
  1033.     }
  1034.     else
  1035.     {
  1036.     // Better be a meta event.
  1037.     //  BYTE    byEvent
  1038.     //  BYTE    byEventType
  1039.     //  VDWORD  dwEventLength
  1040.     //  BYTE    pLongEventData[dwEventLength]
  1041.         //
  1042.         assert( pteTemp->byShortData[0] == MIDI_META );
  1043.  
  1044.         // The only meta-event we care about is change tempo
  1045.         //
  1046.         if( pteTemp->byShortData[1] != MIDI_META_TEMPO )
  1047.             {
  1048.         if( dwMallocBlocks )
  1049.         {
  1050.         free( pteTemp->pLongData );
  1051.         dwMallocBlocks--;
  1052.         }
  1053.             return( CONVERTERR_METASKIP );
  1054.         }
  1055.  
  1056.         // We should have three bytes of parameter data...
  1057.         assert( pteTemp->dwEventLength == 3 );
  1058.  
  1059.         // Need 3 DWORD's: delta-t, stream-ID, event data
  1060.     if( lpciInfo->dwMaxLength - lpciInfo->dwBytesRecorded < 3 *sizeof(DWORD))
  1061.         {
  1062.         // Cleanup the temporary event if necessary and return
  1063.         if( dwMallocBlocks )
  1064.         {
  1065.         free( pteTemp->pLongData );
  1066.         dwMallocBlocks--;
  1067.         }
  1068.         return( CONVERTERR_BUFFERFULL );
  1069.         }
  1070.  
  1071.     pmeEvent->dwDeltaTime = tkDelta;
  1072.     pmeEvent->dwStreamID = 0;
  1073.     // Note: this is backwards from above because we're converting a single
  1074.     //       data value from hi-lo to lo-hi format...
  1075.     pmeEvent->dwEvent = ( pteTemp->pLongData[2] )
  1076.                 | (((DWORD)pteTemp->pLongData[1] ) << 8 )
  1077.                 | (((DWORD)pteTemp->pLongData[0] ) << 16 );
  1078.  
  1079.     /* This next step has absolutely nothing to do with the conversion of a
  1080.      * MIDI file to a stream, it's simply put here to add the functionality
  1081.      * of the tempo slider. If you don't need this, be sure to remove the
  1082.      * next two lines.
  1083.      */
  1084.     dwCurrentTempo = pmeEvent->dwEvent;
  1085.     pmeEvent->dwEvent = (pmeEvent->dwEvent * 100 ) / dwTempoMultiplier;
  1086.  
  1087.     pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
  1088.  
  1089.     dwBufferTickLength = ( ifs.dwTimeDivision * 1000 * BUFFER_TIME_LENGTH ) / dwCurrentTempo;
  1090.     wsprintf( szTemp, "dwBufferTickLength = %lu", dwBufferTickLength );
  1091.     DebugPrint( szTemp );
  1092.  
  1093.     if( dwMallocBlocks )
  1094.         {
  1095.         free( pteTemp->pLongData );
  1096.         dwMallocBlocks--;
  1097.         }
  1098.     lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
  1099.     }
  1100.  
  1101.     return( FALSE );
  1102.     }
  1103.  
  1104.  
  1105. #ifdef DEBUG
  1106. static void ShowTrackError( PINTRACKSTATE ptsTrack, LPSTR lpszErr )
  1107.     {
  1108.     wsprintf( szTemp, "Track buffer offset %lu",
  1109.                 (DWORD)(ptsTrack->pTrackCurrent - ptsTrack->pTrackStart));
  1110.     DebugPrint( szTemp );
  1111.     wsprintf( szTemp, "Track total %lu  Track left %lu",
  1112.                 ptsTrack->dwTrackLength, ptsTrack->dwLeftInBuffer );
  1113.     DebugPrint( szTemp );
  1114.     }
  1115.  
  1116. #endif
  1117.  
  1118.