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

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:   mid2strm.c
  6.  *  Content:    Converts a MIDI file into a MDS (MidiStream) File.
  7.  *
  8.  ***************************************************************************/
  9. #include <stdio.h>
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. #include <mmsystem.h>
  13. #include <assert.h>
  14.  
  15. // MIDI file constants
  16. //
  17. #define MThd            0x6468544D              // Start of file
  18. #define MTrk            0x6B72544D              // Start of track
  19.  
  20. #define MIDI_SYSEX      ((BYTE)0xF0)            // SysEx begin
  21. #define MIDI_SYSEXEND   ((BYTE)0xF7)            // SysEx begin
  22. #define MIDI_META       ((BYTE)0xFF)            // Meta event begin
  23. #define MIDI_META_TEMPO ((BYTE)0x51)
  24. #define MIDI_META_EOT   ((BYTE)0x2F)            // End-of-track
  25.  
  26. #define MIDI_NOTEOFF    ((BYTE)0x80)            // + note + velocity
  27. #define MIDI_NOTEON     ((BYTE)0x90)            // + note + velocity
  28. #define MIDI_POLYPRESS  ((BYTE)0xA0)            // + pressure (2 bytes)
  29. #define MIDI_CTRLCHANGE ((BYTE)0xB0)            // + ctrlr + value
  30. #define MIDI_PRGMCHANGE ((BYTE)0xC0)            // + new patch
  31. #define MIDI_CHANPRESS  ((BYTE)0xD0)            // + pressure (1 byte)
  32. #define MIDI_PITCHBEND  ((BYTE)0xE0)            // + pitch bend (2 bytes)
  33.  
  34. #define CB_STREAMBUF    (4096)                  // Size of each stream buffer
  35.  
  36. #define MIDS_SHORTMSG   (0x00000000)
  37. #define MIDS_TEMPO      (0x01000000)
  38.  
  39. // Macros for swapping hi/lo-endian data
  40. //
  41. #define WORDSWAP(w)     (((w) >> 8) | \
  42.                         (((w) << 8) & 0xFF00))
  43.  
  44. #define DWORDSWAP(dw)   (((dw) >> 24) |                 \
  45.                         (((dw) >> 8) & 0x0000FF00) |    \
  46.                         (((dw) << 8) & 0x00FF0000) |    \
  47.                         (((dw) << 24) & 0xFF000000))
  48.  
  49. // In debug builds, TRACKERR will show us where the parser died
  50. //
  51. #ifdef _DEBUG
  52. #define TRACKERR(p,sz) ShowTrackError(p,sz);
  53. #else
  54. #define TRACKERR(p,sz)
  55. #endif
  56.  
  57.  
  58. // These structures are stored in MIDI files; they need to be byte
  59. // aligned.
  60. //
  61. #pragma pack(1)
  62.  
  63. // Chunk header. dwTag is either MTrk or MThd.
  64. //
  65. typedef struct
  66. {
  67.     DWORD           dwTag;                  // Type
  68.     DWORD           cbChunk;                // Length (hi-lo)
  69. } MIDICHUNK;
  70.  
  71. // Contents of MThd chunk.
  72. typedef struct
  73. {
  74.     WORD            wFormat;                // Format (hi-lo)
  75.     WORD            cTrack;                 // # tracks (hi-lo)
  76.     WORD            wTimeDivision;          // Time division (hi-lo)
  77. } MIDIFILEHDR;
  78.  
  79. #pragma pack()
  80.  
  81. // One event we're reading or writing to a track
  82. //
  83. typedef struct
  84. {
  85.     DWORD           tkEvent;            // Absolute time of event
  86.     BYTE            abEvent[4];         // Event type and parameters if channel msg
  87.     DWORD           cbEvent;            // Of data which follows if meta or sysex
  88.     LPBYTE          pEvent;             // -> Event data if applicable
  89. } MEVENT;   
  90.  
  91. // Description of a track open for read
  92. //
  93. #define ITS_F_ENDOFTRK  0x00000001
  94.  
  95. typedef struct
  96. {
  97.     DWORD           fdwTrack;           // Track status
  98.     DWORD           cbTrack;            // Total bytes in track
  99.     DWORD           cbLeft;             // Bytes left unread in track
  100.     LPBYTE          pTrack;             // -> start of track data
  101.     LPBYTE          pTrackPointer;      // -> next byte to read
  102.     DWORD           tkNextEventDue;     // Absolute time of next event in track
  103.     BYTE            bRunningStatus;     // Running status from last channel msg
  104. #ifdef _DEBUG
  105.     DWORD           nTrack;             // # of this track for debugging
  106. #endif
  107. } INTRACKSTATE;
  108.  
  109. // Description of the input MIDI file
  110. //
  111. typedef struct
  112. {
  113.     DWORD           cbFile;             // Total bytes in file
  114.     LPBYTE          pFile;              // -> entire file in memory
  115.     DWORD           cbLeft;             // Bytes left unread
  116.     LPBYTE          pFilePointer;       // -> next byte to read
  117.  
  118.     DWORD           dwTimeDivision;     // Original time division
  119.     DWORD           dwFormat;           // Original format
  120.     DWORD           cTrack;             // Track count (specifies apIts size)
  121.     INTRACKSTATE*   apIts;              // -> array of tracks in this file
  122. } INFILESTATE;
  123.  
  124. // Description of a stream buffer on the output side
  125. //
  126. typedef struct STREAMBUF *PSTREAMBUF;
  127. typedef struct STREAMBUF
  128. {
  129.     LPBYTE          pBuffer;            // -> Start of actual buffer
  130.     DWORD           tkStart;            // Tick time just before first event
  131.     LPBYTE          pbNextEvent;        // Where to write next event
  132.     DWORD           cbLeft;             // bytes left in buffer
  133.     DWORD           cbLeftUncompressed; // bytes left when uncompressed
  134.     PSTREAMBUF      pNext;              // Next buffer
  135. } STREAMBUF;
  136.  
  137. // Description of output stream open for write
  138. //
  139. typedef struct
  140. {
  141.     DWORD           tkTrack;            // Current tick position in track   
  142.     PSTREAMBUF      pFirst;             // First stream buffer
  143.     PSTREAMBUF      pLast;              // Last (current) stream buffer
  144. } OUTSTREAMSTATE;
  145.  
  146. // Format of structs within a MSD file
  147. //
  148. // 'fmt ' chunk
  149. //
  150.  
  151. #define MDS_F_NOSTREAMID    0x00000001  // Stream ID's skipped; reader inserts
  152. typedef struct
  153. {
  154.     DWORD           dwTimeFormat;       // Low word == time format in SMF format
  155.     DWORD           cbMaxBuffer;        // Guaranteed max buffer size
  156.     DWORD           dwFlags;            // Format flags
  157. } MIDSFMT;
  158.  
  159. // 'data' chunk buffer header
  160. //
  161. typedef struct
  162. {
  163.     DWORD           tkStart;            // Absolute tick offset at start of buffer
  164.     DWORD           cbBuffer;           // Bytes in this buffer
  165. } MIDSBUFFER;
  166.  
  167. // A few globals
  168. //
  169. static HANDLE       hInFile             = INVALID_HANDLE_VALUE;
  170. static HANDLE       hOutFile            = INVALID_HANDLE_VALUE;
  171. static INFILESTATE  ifs;
  172. static OUTSTREAMSTATE ots;
  173. static BOOL         fCompress           = FALSE;
  174.  
  175. // Messages
  176. //
  177. static char szInitErrMem[]          = "Out of memory.\n";
  178. static char szInitErrInFile[]       = "Read error on input file or file is corrupt.\n";
  179.  
  180. #ifdef _DEBUG
  181. static char gteBadRunStat[] =       "Reference to missing running status.";
  182. static char gteRunStatMsgTrunc[] =  "Running status message truncated";
  183. static char gteChanMsgTrunc[] =     "Channel message truncated";
  184. static char gteSysExLenTrunc[] =    "SysEx event truncated (length)";
  185. static char gteSysExTrunc[] =       "SysEx event truncated";
  186. static char gteMetaNoClass[] =      "Meta event truncated (no class byte)";
  187. static char gteMetaLenTrunc[] =     "Meta event truncated (length)";
  188. static char gteMetaTrunc[] =        "Meta event truncated";
  189. #endif
  190.  
  191. // Prototypes
  192. //
  193. static BOOL         Init(LPSTR szInFile, LPSTR szOutFile);
  194. static LPBYTE       GetInFileData(DWORD cbToGet);
  195. static void         Cleanup(void);
  196. static BOOL         BuildNewTracks(void);
  197. static BOOL         WriteStreamBuffers(void);
  198. static BOOL         GetTrackVDWord(INTRACKSTATE* pTs, LPDWORD lpdw);
  199. static BOOL         GetTrackEvent(INTRACKSTATE* pTs, MEVENT *pMe);
  200. static BOOL         AddEventToStream(MEVENT *pMe);
  201. static LPBYTE       GetOutStreamBytes(DWORD tkNow, DWORD cbNeeded, DWORD cbUncompressed);
  202. #ifdef _DEBUG
  203. static void         ShowTrackError(INTRACKSTATE* pTs, char* szErr);
  204. #endif
  205.  
  206. void main(int argc, char* argv[])
  207. {
  208.     UINT            idxFnames;
  209.  
  210.     if (argc < 3)
  211.     {
  212.         fprintf(stderr, "Format is mid2strm [-c] infile outfile\n");
  213.         fprintf(stderr, "-c\tNo-stream-id compression\n");
  214.         exit(1);
  215.     }
  216.  
  217.     idxFnames = 1;
  218.     if (argv[1][0] == '-')
  219.     {
  220.         ++idxFnames;
  221.         if (argv[1][1] == 'c')
  222.             fCompress = TRUE;
  223.     }
  224.  
  225.     if (!Init(argv[idxFnames], argv[idxFnames+1]))
  226.         exit(1);
  227.  
  228.     if (!BuildNewTracks())
  229.         exit(1);
  230.  
  231.     if (!WriteStreamBuffers())
  232.         exit(1);
  233.  
  234.     // Add cleanup code!!!
  235.     //
  236.     Cleanup();
  237.  
  238.     exit(0);
  239. }
  240.  
  241. // Init
  242. // 
  243. // Open the input and output files
  244. // Allocate and read the entire input file into memory
  245. // Validate the input file structure
  246. // Allocate the input track structures and initialize them
  247. // Initialize the output track structures
  248. //
  249. // Return TRUE on success
  250. // Prints its own error message if something goes wrong
  251. //
  252. static BOOL Init(LPSTR szInFile, LPSTR szOutFile)
  253. {
  254.     BOOL            fRet = FALSE;
  255.     DWORD           cbRead;
  256.     LPDWORD         lpdwTag;
  257.     LPDWORD         lpcbHeader;
  258.     DWORD           cbHeader;
  259.     MIDIFILEHDR*    pHeader;
  260.     INTRACKSTATE*   pTs;
  261.     UINT            idx;
  262.     
  263.     // Initialize things we'll try to free later if we fail
  264.     //
  265.     ifs.cbFile = 0;
  266.     ifs.pFile = NULL;
  267.     ifs.apIts = NULL;
  268.  
  269.     // Attempt to open the input and output files
  270.     //
  271.     hInFile = CreateFile(szInFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  272.     if (INVALID_HANDLE_VALUE == hInFile)
  273.     {
  274.         fprintf(stderr, "Could not open \"%s\" for read.\n", szInFile);
  275.         goto Init_Cleanup;
  276.     }
  277.  
  278.     hOutFile = CreateFile(szOutFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  279.     if (INVALID_HANDLE_VALUE == hOutFile)
  280.     {
  281.         fprintf(stderr, "Could not open \"%s\" for write.\n", szOutFile);
  282.         goto Init_Cleanup;
  283.     }
  284.  
  285.     // Figure out how big the input file is and allocate a chunk of memory big enough
  286.     // to hold the whole thing. Read the whole file in at once.
  287.     //
  288.     if (((UINT)-1) == (ifs.cbFile = GetFileSize(hInFile, NULL)))
  289.     {
  290.         fprintf(stderr, "File system error on input file.\n");
  291.         goto Init_Cleanup;
  292.     }
  293.  
  294.     if (NULL == (ifs.pFile = GlobalAllocPtr(GPTR, ifs.cbFile)))
  295.     {
  296.         fprintf(stderr, szInitErrMem);
  297.         goto Init_Cleanup;
  298.     }
  299.  
  300.     if ((!ReadFile(hInFile, ifs.pFile, ifs.cbFile, &cbRead, NULL)) ||
  301.         cbRead != ifs.cbFile)
  302.     {
  303.         fprintf(stderr, szInitErrInFile);
  304.         goto Init_Cleanup;
  305.     }
  306.     
  307.     // Set up to read from the memory buffer. Read and validate
  308.     // - MThd header
  309.     // - size of file header chunk
  310.     // - file header itself
  311.     //  
  312.     ifs.cbLeft = ifs.cbFile;
  313.     ifs.pFilePointer = ifs.pFile;
  314.  
  315.     if (NULL == (lpdwTag = (LPDWORD)GetInFileData(sizeof(*lpdwTag))) ||
  316.         *lpdwTag != MThd ||
  317.         NULL == (lpcbHeader = (LPDWORD)GetInFileData(sizeof(*lpcbHeader))) ||
  318.         (cbHeader = DWORDSWAP(*lpcbHeader)) < sizeof(MIDIFILEHDR) ||
  319.         NULL == (pHeader = (MIDIFILEHDR*)GetInFileData(cbHeader)))
  320.     {
  321.         fprintf(stderr, szInitErrInFile);
  322.         goto Init_Cleanup;
  323.     }
  324.  
  325.     // File header is stored in hi-lo order. Swap this into Intel order and save
  326.     // parameters in our native int size (32 bits)
  327.     //
  328.     ifs.dwFormat = (DWORD)WORDSWAP(pHeader->wFormat);
  329.     ifs.cTrack = (DWORD)WORDSWAP(pHeader->cTrack);
  330.     ifs.dwTimeDivision = (DWORD)WORDSWAP(pHeader->wTimeDivision);
  331.  
  332.     // We know how many tracks there are; allocate the structures for them and parse
  333.     // them. The parse merely looks at the MTrk signature and track chunk length
  334.     // in order to skip to the next track header.
  335.     //
  336.     ifs.apIts = (INTRACKSTATE*)GlobalAllocPtr(GPTR, ifs.cTrack*sizeof(INTRACKSTATE));
  337.     if (NULL == ifs.apIts)
  338.     {
  339.         fprintf(stderr, szInitErrMem);
  340.         goto Init_Cleanup;
  341.     }
  342.  
  343.     for (idx = 0, pTs = ifs.apIts; idx < ifs.cTrack; ++idx, ++pTs)
  344.     {
  345.         if (NULL == (lpdwTag = (LPDWORD)GetInFileData(sizeof(*lpdwTag))) ||
  346.             *lpdwTag != MTrk ||
  347.             NULL == (lpcbHeader = (LPDWORD)GetInFileData(sizeof(*lpcbHeader))))
  348.         {
  349.             fprintf(stderr, szInitErrInFile);
  350.             goto Init_Cleanup;
  351.         }
  352.  
  353.         cbHeader = DWORDSWAP(*lpcbHeader);
  354.         pTs->cbTrack = cbHeader;
  355.         pTs->cbLeft = cbHeader;
  356.         pTs->pTrack = GetInFileData(cbHeader);
  357.         if (NULL == pTs->pTrack)
  358.         {
  359.             fprintf(stderr, szInitErrInFile);
  360.             goto Init_Cleanup;
  361.         }
  362.  
  363. #ifdef _DEBUG
  364.         pTs->nTrack = idx;
  365. #endif
  366.         pTs->pTrackPointer = pTs->pTrack;
  367.         pTs->cbLeft = pTs->cbTrack;
  368.         pTs->fdwTrack = 0;
  369.         pTs->bRunningStatus = 0;
  370.  
  371.         // Handle bozo MIDI files which contain empty track chunks
  372.         //
  373.         if (!pTs->cbLeft)
  374.         {
  375.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  376.             continue;
  377.         }
  378.  
  379.  
  380.         // We always preread the time from each track so the mixer code can
  381.         // determine which track has the next event with a minimum of work
  382.         //
  383.         if (!GetTrackVDWord(pTs, &pTs->tkNextEventDue))
  384.         {
  385.             fprintf(stderr, szInitErrInFile);
  386.             goto Init_Cleanup;
  387.         }
  388.     }
  389.  
  390.     ots.tkTrack = 0;
  391.     ots.pFirst = NULL;
  392.     ots.pLast = NULL;
  393.  
  394.     fRet = TRUE;
  395.         
  396. Init_Cleanup:
  397.     if (!fRet)
  398.         Cleanup();
  399.  
  400.     return fRet;
  401. }
  402.  
  403. //
  404. // GetInFileData
  405. //
  406. // Gets the requested number of bytes of data from the input file and returns
  407. // a pointer to them.
  408. // 
  409. // Returns a pointer to the data or NULL if we'd read more than is
  410. // there.
  411. //
  412. static LPBYTE       GetInFileData(DWORD cbToGet)
  413. {
  414.     LPBYTE              pRet;
  415.  
  416.     if (ifs.cbLeft < cbToGet)
  417.         return NULL;
  418.  
  419.     pRet = ifs.pFilePointer;
  420.  
  421.     ifs.cbLeft -= cbToGet;
  422.     ifs.pFilePointer += cbToGet;
  423.  
  424.     return pRet;
  425. }
  426.  
  427. //
  428. // Cleanup
  429. //
  430. // Free anything we ever allocated
  431. //
  432. static void         Cleanup(void)
  433. {
  434.     PSTREAMBUF          pCurr;
  435.     PSTREAMBUF          pNext;
  436.  
  437.     if (hInFile != INVALID_HANDLE_VALUE)    CloseHandle(hInFile);
  438.     if (hOutFile != INVALID_HANDLE_VALUE)   CloseHandle(hOutFile);
  439.     if (ifs.pFile)                          GlobalFreePtr(ifs.pFile);
  440.     if (ifs.apIts)                          GlobalFreePtr(ifs.apIts);
  441.     
  442.  
  443.     pCurr = ots.pFirst;
  444.     while (pCurr)
  445.     {
  446.         pNext = pCurr->pNext;
  447.         GlobalFreePtr(pCurr);
  448.         pCurr = pNext;
  449.     }
  450. }
  451.  
  452. //
  453. // BuildNewTracks
  454. //
  455. // This is where the actual work gets done.
  456. //
  457. // Until all tracks are done,
  458. //  Scan the tracks to find the next due event
  459. //  Figure out where the event belongs in the new mapping
  460. //  Put it there
  461. // Add end of track metas to all new tracks that now have any data
  462. //
  463. // Return TRUE on success
  464. // Prints its own error message if something goes wrong
  465. //
  466. static BOOL         BuildNewTracks(void)
  467. {
  468.     INTRACKSTATE*       pTs;
  469.     INTRACKSTATE*       pTsFound;
  470.     UINT                idx;
  471.     DWORD               tkNext;
  472.     MEVENT          me;
  473.     
  474.     for(;;)
  475.     {
  476.         // Find nearest event due
  477.         //
  478.         pTsFound = NULL;
  479.         tkNext = 0xFFFFFFFFL;
  480.  
  481.         for (idx = 0, pTs = ifs.apIts; idx < ifs.cTrack; ++idx, ++pTs)
  482.             if ((!(pTs->fdwTrack & ITS_F_ENDOFTRK)) && (pTs->tkNextEventDue < tkNext))
  483.             {
  484.                 tkNext = pTs->tkNextEventDue;
  485.                 pTsFound = pTs;
  486.             }
  487.  
  488.         // None found? We must be done
  489.         //
  490.         if (!pTsFound)
  491.             break;
  492.  
  493.         // Ok, get the event header from that track
  494.         //
  495.  
  496.         if (!GetTrackEvent(pTsFound, &me))
  497.         {
  498.             fprintf(stderr, "MIDI file is corrupt!\n");
  499.             return FALSE;
  500.         }
  501.  
  502.         // Don't add end of track event 'til we're done
  503.         //
  504.         if (me.abEvent[0] == MIDI_META && me.abEvent[1] == MIDI_META_EOT)
  505.             continue;
  506.         
  507.         if (!AddEventToStream(&me))
  508.         {
  509.             fprintf(stderr, "Out of memory building tracks.\n");
  510.             return FALSE;
  511.         }
  512.     }   
  513.  
  514.     return TRUE;
  515. }
  516.  
  517. //
  518. // WriteStreamBuffers
  519. //
  520. // Write stream buffers into an MDS file (RIFF MIDS format)
  521. //
  522. // Return TRUE on success
  523. // Prints its own error message if something goes wrong
  524. //
  525. #define FOURCC_MIDS mmioFOURCC('M','I','D','S')
  526. #define FOURCC_fmt  mmioFOURCC('f','m','t',' ')
  527. #define FOURCC_data mmioFOURCC('d','a','t','a')
  528.  
  529. static BOOL         WriteStreamBuffers(void)
  530. {
  531.     DWORD           cbFmt;
  532.     DWORD           cbData;
  533.     DWORD           cbRiff;
  534.     PSTREAMBUF      psb;
  535.     FOURCC          fcc;
  536.     FOURCC          fcc2;
  537.     MIDSFMT         fmt;
  538.     MIDSBUFFER      data;
  539.     DWORD           cb;
  540.     DWORD           cBuffers;
  541.  
  542.     // Walk buffer list to find entire size of data chunk
  543.     //
  544.     cbData = sizeof(cBuffers);
  545.     cBuffers = 0;
  546.     for (psb = ots.pFirst; psb; psb = psb->pNext, ++cBuffers)
  547.         cbData += sizeof(MIDSBUFFER) + (CB_STREAMBUF - psb->cbLeft);
  548.  
  549.     cbFmt = sizeof(fmt);
  550.  
  551.     // Figure size of entire RIFF chunk
  552.     //
  553.     cbRiff = 
  554.         sizeof(FOURCC) +            // RIFF form type ('MIDS')
  555.         sizeof(FOURCC) +            // Format chunk type ('fmt ')
  556.         sizeof(DWORD) +             // Format chunk size
  557.         sizeof(MIDSFMT) +           // Format chunk contents
  558.         sizeof(FOURCC) +            // Data chunk type ('data')
  559.         sizeof(DWORD) +             // Data chunk size
  560.         cbData;                     // Data chunk contents
  561.         
  562.     fcc = FOURCC_RIFF;
  563.     fcc2 = FOURCC_MIDS;
  564.     if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
  565.         (!WriteFile(hOutFile, &cbRiff, sizeof(cbRiff), &cb, NULL)) ||
  566.         (!WriteFile(hOutFile, &fcc2, sizeof(fcc2), &cb, NULL)))
  567.         return FALSE;
  568.  
  569.  
  570.     fmt.dwTimeFormat    = ifs.dwTimeDivision;
  571.     fmt.cbMaxBuffer     = CB_STREAMBUF;
  572.     fmt.dwFlags         = 0;
  573.     if (fCompress)
  574.         fmt.dwFlags     |= MDS_F_NOSTREAMID;
  575.  
  576.     fcc = FOURCC_fmt;
  577.     if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
  578.         (!WriteFile(hOutFile, &cbFmt, sizeof(cbFmt), &cb, NULL)) ||
  579.         (!WriteFile(hOutFile, &fmt, sizeof(fmt), &cb, NULL)))
  580.         return FALSE;
  581.  
  582.     fcc = FOURCC_data;
  583.     if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
  584.         (!WriteFile(hOutFile, &cbData, sizeof(cbData), &cb, NULL)) ||
  585.         (!WriteFile(hOutFile, &cBuffers, sizeof(cBuffers), &cb, NULL)))
  586.         return FALSE;
  587.  
  588.     for (psb = ots.pFirst; psb; psb = psb->pNext)
  589.     {
  590.         data.tkStart = psb->tkStart;
  591.         data.cbBuffer = CB_STREAMBUF - psb->cbLeft;
  592.  
  593.         if ((!WriteFile(hOutFile, &data, sizeof(data), &cb, NULL)) ||
  594.             (!WriteFile(hOutFile, psb->pBuffer, data.cbBuffer, &cb, NULL)))
  595.             return FALSE;
  596.     }
  597.             
  598.     return TRUE;
  599. }
  600.  
  601. //
  602. // GetTrackVDWord
  603. //
  604. // Attempts to parse a variable length DWORD from the given track. A VDWord
  605. // in a MIDI file
  606. //  (a) is in lo-hi format 
  607. //  (b) has the high bit set on every byte except the last
  608. //
  609. // Returns the DWORD in *lpdw and TRUE on success; else
  610. // FALSE if we hit end of track first. Sets ITS_F_ENDOFTRK
  611. // if we hit end of track.
  612. //
  613. static BOOL         GetTrackVDWord(INTRACKSTATE* pTs, LPDWORD lpdw)
  614. {
  615.     BYTE                b;
  616.     DWORD               dw = 0;
  617.  
  618.     if (pTs->fdwTrack & ITS_F_ENDOFTRK)
  619.         return FALSE;
  620.  
  621.     do
  622.     {
  623.         if (!pTs->cbLeft)
  624.         {
  625.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  626.             return FALSE;
  627.         }
  628.  
  629.         b = *pTs->pTrackPointer++;
  630.         --pTs->cbLeft;
  631.  
  632.         dw = (dw << 7) | (b & 0x7F);
  633.     } while (b & 0x80);
  634.  
  635.     *lpdw = dw;
  636.  
  637.     return TRUE;
  638. }
  639.  
  640. //
  641. // GetTrackEvent
  642. //
  643. // Fills in the event struct with the next event from the track
  644. //
  645. // pMe->tkEvent will contain the absolute tick time of the event
  646. // pMe->abEvent[0] will contain
  647. //  MIDI_META if the event is a meta event;
  648. //   in this case pMe->abEvent[1] will contain the meta class
  649. //  MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event
  650. //  Otherwise, the event is a channel message and pMe->abEvent[1]
  651. //   and pMe->abEvent[2] will contain the rest of the event.
  652. //
  653. // pMe->cbEvent will contain
  654. //  The total length of the channel message in pMe->abEvent if
  655. //   the event is a channel message
  656. //  The total length of the paramter data pointed to by
  657. //   pMe->pEvent otherwise
  658. //
  659. // pMe->pEvent will point at any additional paramters if the 
  660. //  event is a SysEx or meta event with non-zero length; else
  661. //  it will contain NULL
  662. //
  663. // Returns TRUE on success or FALSE on any kind of parse error
  664. // Prints its own error message ONLY in the debug version
  665. //
  666. // Maintains the state of the input track (i.e. pTs->cbLeft,
  667. // pTs->pTrackPointers, and pTs->bRunningStatus).
  668. //
  669. static BOOL         GetTrackEvent(INTRACKSTATE* pTs, MEVENT *pMe)
  670. {
  671.     BYTE                b;
  672.     UINT                cbEvent;
  673.  
  674.     pMe->pEvent = NULL;
  675.  
  676.     // Already at end of track? There's nothing to read.
  677.     //
  678.     if ((pTs->fdwTrack & ITS_F_ENDOFTRK) || !pTs->cbLeft)
  679.         return FALSE;
  680.  
  681.     // Get the first byte, which determines the type of event.
  682.     //
  683.     b = *pTs->pTrackPointer++;
  684.     --pTs->cbLeft;
  685.  
  686.     // If the high bit is not set, then this is a channel message
  687.     // which uses the status byte from the last channel message
  688.     // we saw. NOTE: We do not clear running status across SysEx or
  689.     // meta events even though the spec says to because there are
  690.     // actually files out there which contain that sequence of data.
  691.     //
  692.     if (!(b & 0x80))
  693.     {
  694.         // No previous status byte? We're hosed.
  695.         //
  696.         if (!pTs->bRunningStatus)
  697.         {
  698.             TRACKERR(pTs, gteBadRunStat);
  699.             return FALSE;
  700.         }
  701.  
  702.         pMe->abEvent[0] = pTs->bRunningStatus;
  703.         pMe->abEvent[1] = b;
  704.  
  705.         b = pMe->abEvent[0] & 0xF0;
  706.         pMe->cbEvent = 2;
  707.  
  708.         // Only program change and channel pressure events are 2 bytes long;
  709.         // the rest are 3 and need another byte
  710.         //
  711.         if (b != MIDI_PRGMCHANGE && b != MIDI_CHANPRESS)
  712.         {
  713.             if (!pTs->cbLeft)
  714.             {
  715.                 TRACKERR(pTs, gteRunStatMsgTrunc);
  716.                 pTs->fdwTrack |= ITS_F_ENDOFTRK;
  717.                 return FALSE;
  718.             }
  719.  
  720.             pMe->abEvent[2] = *pTs->pTrackPointer++;
  721.             --pTs->cbLeft;
  722.             ++pMe->cbEvent;
  723.         }
  724.     }
  725.     else if ((b & 0xF0) != MIDI_SYSEX)
  726.     {
  727.         // Not running status, not in SysEx range - must be
  728.         // normal channel message (0x80-0xEF)
  729.         //
  730.         pMe->abEvent[0] = b;
  731.         pTs->bRunningStatus = b;
  732.         
  733.         // Strip off channel and just keep message type
  734.         //
  735.         b &= 0xF0;
  736.  
  737.         cbEvent = (b == MIDI_PRGMCHANGE || b == MIDI_CHANPRESS) ? 1 : 2;
  738.         pMe->cbEvent = cbEvent + 1;
  739.  
  740.         if (pTs->cbLeft < cbEvent)
  741.         {
  742.             TRACKERR(pTs, gteChanMsgTrunc);
  743.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  744.             return FALSE;
  745.         }
  746.  
  747.         pMe->abEvent[1] = *pTs->pTrackPointer++;
  748.         if (cbEvent == 2)
  749.             pMe->abEvent[2] = *pTs->pTrackPointer++;
  750.  
  751.         pTs->cbLeft -= cbEvent;
  752.     } 
  753.     else if (b == MIDI_SYSEX || b == MIDI_SYSEXEND)
  754.     {
  755.         // One of the SysEx types. (They are the same as far as we're concerned;
  756.         // there is only a semantic difference in how the data would actually
  757.         // get sent when the file is played. We must take care to put the correct
  758.         // event type back on the output track, however.)
  759.         //
  760.         // Parse the general format of:
  761.         //  BYTE    bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
  762.         //  VDWORD  cbParms
  763.         //  BYTE    abParms[cbParms]
  764.         //
  765.         pMe->abEvent[0] = b;
  766.         if (!GetTrackVDWord(pTs, &pMe->cbEvent))
  767.         {
  768.             TRACKERR(pTs, gteSysExLenTrunc);
  769.             return FALSE;
  770.         }
  771.         
  772.         if (pTs->cbLeft < pMe->cbEvent)
  773.         {
  774.             TRACKERR(pTs, gteSysExTrunc);
  775.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  776.             return FALSE;
  777.         }
  778.  
  779.         pMe->pEvent = pTs->pTrackPointer;
  780.         pTs->pTrackPointer += pMe->cbEvent;
  781.         pTs->cbLeft -= pMe->cbEvent;
  782.     } 
  783.     else if (b == MIDI_META)
  784.     {
  785.         // It's a meta event. Parse the general form:
  786.         //  BYTE    bEvent  (MIDI_META)
  787.         //  BYTE    bClass
  788.         //  VDWORD  cbParms
  789.         //  BYTE    abParms[cbParms]
  790.         //
  791.         pMe->abEvent[0] = b;
  792.  
  793.         if (!pTs->cbLeft)
  794.         {
  795.             TRACKERR(pTs, gteMetaNoClass);
  796.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  797.             return FALSE;
  798.         }
  799.  
  800.         pMe->abEvent[1] = *pTs->pTrackPointer++;
  801.         --pTs->cbLeft;
  802.  
  803.         if (!GetTrackVDWord(pTs, &pMe->cbEvent))
  804.         {   
  805.             TRACKERR(pTs, gteMetaLenTrunc);
  806.             return FALSE;
  807.         }
  808.  
  809.         // NOTE: Perfectly valid to have a meta with no data
  810.         // In this case, cbEvent == 0 and pEvent == NULL
  811.         //
  812.         if (pMe->cbEvent)
  813.         {       
  814.             if (pTs->cbLeft < pMe->cbEvent)
  815.             {
  816.                 TRACKERR(pTs, gteMetaTrunc);
  817.                 pTs->fdwTrack |= ITS_F_ENDOFTRK;
  818.                 return FALSE;
  819.             }
  820.  
  821.             pMe->pEvent = pTs->pTrackPointer;
  822.             pTs->pTrackPointer += pMe->cbEvent;
  823.             pTs->cbLeft -= pMe->cbEvent;
  824.         }
  825.  
  826.         if (pMe->abEvent[1] == MIDI_META_EOT)
  827.             pTs->fdwTrack |= ITS_F_ENDOFTRK;
  828.     }
  829.     else
  830.     {
  831.         // Messages in this range are system messages and aren't supposed to
  832.         // be in a normal MIDI file. If they are, we've misparsed or the
  833.         // authoring software is stpuid.
  834.         //
  835.         return FALSE;
  836.     }
  837.  
  838.     // Event time was already stored as the current track time
  839.     //
  840.     pMe->tkEvent = pTs->tkNextEventDue;
  841.  
  842.     // Now update to the next event time. The code above MUST properly
  843.     // maintain the end of track flag in case the end of track meta is
  844.     // missing. 
  845.     //
  846.     if (!(pTs->fdwTrack & ITS_F_ENDOFTRK))
  847.     {
  848.         DWORD               tkDelta;
  849.  
  850.         if (!GetTrackVDWord(pTs, &tkDelta))
  851.             return FALSE;
  852.  
  853.         pTs->tkNextEventDue += tkDelta;
  854.     }
  855.  
  856.     return TRUE;
  857. }
  858.  
  859. //
  860. // AddEventToStream
  861. //
  862. // Put the given event onto the given output track.
  863. // pMe must point to an event filled out in accordance with the
  864. // description given in GetTrackEvent
  865. //
  866. // Returns TRUE on sucess or FALSE if we're out of memory
  867. //
  868. static BOOL         AddEventToStream(MEVENT *pMe)
  869. {
  870.     PDWORD                  pdw;        
  871.     DWORD                   tkNow;
  872.     DWORD                   tkDelta;    
  873.     UINT                    cdw;
  874.  
  875.     tkNow = ots.tkTrack;
  876.  
  877.     // Delta time is absolute event time minus absolute time
  878.     // already gone by on this track
  879.     //
  880.     tkDelta = pMe->tkEvent - ots.tkTrack;
  881.  
  882.     // Event time is now current time on this track
  883.     //
  884.     ots.tkTrack = pMe->tkEvent;
  885.  
  886.     if (pMe->abEvent[0] < MIDI_SYSEX)
  887.     {
  888.         // Channel message. We know how long it is, just copy it. Need 3 DWORD's: delta-t, 
  889.         // stream-ID, event
  890.         // 
  891.         // TODO: Compress with running status
  892.         //
  893.  
  894.         cdw = (fCompress ? 2 : 3);
  895.         if (NULL == (pdw = (PDWORD)GetOutStreamBytes(tkNow, cdw * sizeof(DWORD), 3 * sizeof(DWORD))))
  896.             return FALSE;
  897.  
  898.         *pdw++ = tkDelta;
  899.         if (!fCompress)
  900.             *pdw++ = 0;
  901.         *pdw =  (pMe->abEvent[0]) |
  902.                 (((DWORD)pMe->abEvent[1]) << 8) |
  903.                 (((DWORD)pMe->abEvent[2]) << 16) |
  904.                 MIDS_SHORTMSG;
  905.  
  906.     }
  907.     else if (pMe->abEvent[0] == MIDI_SYSEX || pMe->abEvent[0] == MIDI_SYSEXEND)
  908.     {
  909.         fprintf(stderr, "NOTE: Ignoring SysEx for now.\n");
  910.     }
  911.     else
  912.     {
  913.         // Better be a meta event.
  914.         //  BYTE        bEvent
  915.         //  BYTE        bClass
  916.         //  VDWORD      cbParms
  917.         //  BYTE        abParms[cbParms]
  918.         //
  919.         assert(pMe->abEvent[0] == MIDI_META);
  920.  
  921.         // The only meta-event we care about is change tempo
  922.         //
  923.         if (pMe->abEvent[1] != MIDI_META_TEMPO)
  924.             return TRUE;
  925.  
  926.         assert(pMe->cbEvent == 3);
  927.  
  928.         cdw = (fCompress ? 2 : 3);
  929.         pdw = (PDWORD)GetOutStreamBytes(tkNow, cdw * sizeof(DWORD), 3 * sizeof(DWORD));
  930.         if (NULL == pdw)
  931.             return FALSE;
  932.  
  933.         *pdw++ = tkDelta;
  934.         if (!fCompress)
  935.             *pdw++ = (DWORD)-1;
  936.         *pdw =  (pMe->pEvent[2]) |
  937.                 (((DWORD)pMe->pEvent[1]) << 8) |
  938.                 (((DWORD)pMe->pEvent[0]) << 16) |
  939.                 MIDS_TEMPO;
  940.     }
  941.  
  942.     return TRUE;    
  943. }
  944.  
  945. //
  946. // GetOutStreamBytes
  947. //
  948. // This function performs the memory management and pseudo-file I/O for output
  949. // tracks.
  950. // 
  951. // We build a linked list of stream buffers as they would exist if they were
  952. // about to be played. Each buffer is CB_STREAMBUF bytes long maximum. They are
  953. // filled as full as possible; events are not allowed to cross buffers.
  954. //
  955. // Returns a pointer to the number of requested bytes or NULL if we're out of memory
  956. //
  957. static LPBYTE       GetOutStreamBytes(DWORD tkNow, DWORD cbNeeded, DWORD cbUncompressed)
  958. {
  959.     LPBYTE                  pb;
  960.  
  961.     // Round request up to the next DWORD boundry. This aligns the final output buffer correctly
  962.     // and allows the above routines to deal with byte-aligned data
  963.     //
  964.     cbNeeded = (cbNeeded + 3) & ~3;
  965.     cbUncompressed = (cbUncompressed + 3) & ~3;
  966.  
  967.     assert(cbUncompressed >= cbNeeded);
  968.  
  969.     if (NULL == ots.pLast || cbUncompressed > ots.pLast->cbLeftUncompressed)
  970.     {
  971.         PSTREAMBUF          pNew;
  972.  
  973.         pNew = GlobalAllocPtr(GHND, sizeof(*pNew) + CB_STREAMBUF);
  974.         if (NULL == pNew)
  975.             return NULL;
  976.             
  977.         pNew->pBuffer       = (LPBYTE)(pNew + 1);
  978.         pNew->tkStart       = tkNow;
  979.         pNew->pbNextEvent   = pNew->pBuffer;
  980.         pNew->cbLeft        = CB_STREAMBUF;
  981.         pNew->cbLeftUncompressed = CB_STREAMBUF;
  982.         pNew->pNext         = NULL;
  983.  
  984.         if (!ots.pLast)
  985.         {
  986.             ots.pFirst = pNew;
  987.             ots.pLast  = pNew;
  988.         }
  989.         else
  990.         {
  991.             ots.pLast->pNext    = pNew;
  992.             ots.pLast           = pNew;
  993.         }
  994.     }
  995.  
  996.     // If there's STILL not enough room for the requested block, then an event is bigger than 
  997.     // the buffer size -- this is unacceptable. 
  998.     //
  999.     if (cbNeeded > ots.pLast->cbLeft)
  1000.     {
  1001.         fprintf(stderr, "NOTE: An event requested %lu bytes of memory; the\n", cbNeeded);
  1002.         fprintf(stderr, "      maximum configured buffer size is %lu.\n", (DWORD)CB_STREAMBUF);
  1003.  
  1004.         return NULL;
  1005.     }
  1006.  
  1007.     pb = ots.pLast->pbNextEvent;
  1008.  
  1009.     ots.pLast->pbNextEvent += cbNeeded;
  1010.     ots.pLast->cbLeft -= cbNeeded;
  1011.     ots.pLast->cbLeftUncompressed -= cbUncompressed;
  1012.  
  1013.     return pb;
  1014. }
  1015.  
  1016. #ifdef _DEBUG
  1017. static void             ShowTrackError(INTRACKSTATE* pTs, char* szErr)
  1018. {
  1019.     fprintf(stderr, "Track %u: %s\n", pTs->nTrack, szErr);      
  1020.     fprintf(stderr, "Track offset %lu\n", (DWORD)(pTs->pTrackPointer - pTs->pTrack));
  1021.     fprintf(stderr, "Track total %lu  Track left %lu\n", pTs->cbTrack, pTs->cbLeft);
  1022. }
  1023. #endif
  1024.