home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / TOOLS / MIDIPLYR / SMFREAD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  17.4 KB  |  566 lines

  1. /*****************************************************************************
  2. *
  3. *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  4. *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  5. *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
  6. *  A PARTICULAR PURPOSE.
  7. *
  8. *  Copyright (C) 1993 - 1995 Microsoft Corporation. All Rights Reserved.
  9. *
  10. ******************************************************************************
  11. *
  12. * SMFRead.C
  13. *
  14. * MIDI File access routines.
  15. *
  16. *****************************************************************************/
  17. #include <windows.h>
  18. #include <windowsx.h>
  19. #include <mmsystem.h>
  20. #include <memory.h>
  21. #include "muldiv32.h" 
  22. #include "smf.h"
  23. #include "smfi.h"
  24. #include "debug.h"
  25.  
  26. PRIVATE UINT grbChanMsgLen[] =
  27.     0,                      /* 0x   not a status byte   */
  28.     0,                      /* 1x   not a status byte   */
  29.     0,                      /* 2x   not a status byte   */
  30.     0,                      /* 3x   not a status byte   */
  31.     0,                      /* 4x   not a status byte   */
  32.     0,                      /* 5x   not a status byte   */
  33.     0,                      /* 6x   not a status byte   */
  34.     0,                      /* 7x   not a status byte   */
  35.     3,                      /* 8x   Note off            */
  36.     3,                      /* 9x   Note on             */
  37.     3,                      /* Ax   Poly pressure       */
  38.     3,                      /* Bx   Control change      */
  39.     2,                      /* Cx   Program change      */
  40.     2,                      /* Dx   Chan pressure       */
  41.     3,                      /* Ex   Pitch bend change   */
  42.     0,                      /* Fx   SysEx (see below)   */             
  43. } ;
  44.  
  45. /******************************************************************************
  46. *
  47. * smfBuildFileIndex
  48. *
  49. * Preliminary parsing of a MIDI file.
  50. *
  51. * ppSmf                     - Pointer to a returned SMF structure if the
  52. *                             file is successfully parsed.
  53. *
  54. * Returns
  55. *   SMF_SUCCESS The events were successfully read.
  56. *   SMF_NO_MEMORY Out of memory to build key frames.
  57. *   SMF_INVALID_FILE A disk or parse error occured on the file.
  58. * This function validates the format of and existing MIDI or RMI file
  59. * and builds the handle structure which will refer to it for the
  60. * lifetime of the instance.
  61. *  
  62. * The file header information will be read and verified, and
  63. * smfBuildTrackIndices will be called on every existing track
  64. * to build keyframes and validate the track format.
  65. *
  66. *****************************************************************************/
  67. SMFRESULT FNLOCAL smfBuildFileIndex(
  68.     PSMF BSTACK *           ppSmf)
  69. {
  70.     SMFRESULT               smfrc;
  71.     CHUNKHDR FAR *          pCh;
  72.     FILEHDR FAR *           pFh;
  73.     DWORD                   idx;
  74.     PSMF                    pSmf,
  75.                             pSmfTemp;
  76.     PTRACK                  pTrk;
  77.     WORD                    wMemory;
  78.     DWORD                   dwLeft;
  79.     HPBYTE                  hpbImage;
  80.     
  81.     DWORD                   idxTrack;
  82.     EVENT                   event;
  83.     BOOL                    fFirst;
  84.     DWORD                   dwLength;
  85.     HLOCAL                  hLocal;
  86.     PTEMPOMAPENTRY          pTempo;
  87.  
  88.     assert(ppSmf != NULL);
  89.  
  90.     pSmf = *ppSmf;
  91.  
  92.     assert(pSmf != NULL);
  93.  
  94.     /* MIDI data image is already in hpbImage (already extracted from
  95.     ** RIFF header if necessary).
  96.     */
  97.  
  98.     /* Validate MIDI header
  99.     */
  100.     dwLeft   = pSmf->cbImage;
  101.     hpbImage = pSmf->hpbImage;
  102.     
  103.     if (dwLeft < sizeof(CHUNKHDR))
  104.         return SMF_INVALID_FILE;
  105.  
  106.     pCh = (CHUNKHDR FAR *)hpbImage;
  107.  
  108.     dwLeft   -= sizeof(CHUNKHDR);
  109.     hpbImage += sizeof(CHUNKHDR);
  110.     
  111.     if (pCh->fourccType != FOURCC_MThd)
  112.         return SMF_INVALID_FILE;
  113.  
  114.     dwLength = DWORDSWAP(pCh->dwLength);
  115.     if (dwLength < sizeof(FILEHDR) || dwLength > dwLeft)
  116.         return SMF_INVALID_FILE;
  117.  
  118.     pFh = (FILEHDR FAR *)hpbImage;
  119.  
  120.     dwLeft   -= dwLength;
  121.     hpbImage += dwLength;
  122.     
  123.     pSmf->dwFormat       = (DWORD)(WORDSWAP(pFh->wFormat));
  124.     pSmf->dwTracks       = (DWORD)(WORDSWAP(pFh->wTracks));
  125.     pSmf->dwTimeDivision = (DWORD)(WORDSWAP(pFh->wDivision));
  126.  
  127.     /*
  128.     ** We've successfully parsed the header. Now try to build the track
  129.     ** index.
  130.     ** 
  131.     ** We only check out the track header chunk here; the track will be
  132.     ** preparsed after we do a quick integretiy check.
  133.     */
  134.     wMemory = sizeof(SMF) + (WORD)(pSmf->dwTracks*sizeof(TRACK));
  135.     pSmfTemp = (PSMF)LocalReAlloc((HLOCAL)pSmf, wMemory, LMEM_MOVEABLE|LMEM_ZEROINIT);
  136.  
  137.     if (NULL == pSmfTemp)
  138.     {
  139.         DPF(1, "No memory for extended pSmf");
  140.         return SMF_NO_MEMORY;
  141.     }
  142.  
  143.     pSmf = *ppSmf = pSmfTemp;
  144.     pTrk = pSmf->rTracks;
  145.     
  146.     for (idx=0; idx<pSmf->dwTracks; idx++)
  147.     {
  148.         if (dwLeft < sizeof(CHUNKHDR))
  149.             return SMF_INVALID_FILE;
  150.  
  151.         pCh = (CHUNKHDR FAR *)hpbImage;
  152.  
  153.         dwLeft   -= sizeof(CHUNKHDR);
  154.         hpbImage += sizeof(CHUNKHDR);
  155.  
  156.         if (pCh->fourccType != FOURCC_MTrk)
  157.             return SMF_INVALID_FILE;
  158.         
  159.         pTrk->idxTrack      = (DWORD)(hpbImage - pSmf->hpbImage);
  160.         pTrk->smti.cbLength = DWORDSWAP(pCh->dwLength);
  161.  
  162.         if (pTrk->smti.cbLength > dwLeft)
  163.         {
  164.             DPF(1, "Track longer than file!");
  165.             return SMF_INVALID_FILE;
  166.         }
  167.  
  168.         dwLeft   -= pTrk->smti.cbLength;
  169.         hpbImage += pTrk->smti.cbLength;
  170.  
  171.         pTrk++;
  172.     }
  173.  
  174.     /* File looks OK. Now preparse, doing the following:
  175.     ** (1) Build tempo map so we can convert to/from ticks quickly
  176.     ** (2) Determine actual tick length of file
  177.     ** (3) Validate all events in all tracks
  178.     */ 
  179.     pSmf->tkPosition = 0;
  180.     pSmf->fdwSMF &= ~SMF_F_EOF;
  181.     
  182.     for (pTrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; pTrk++)
  183.     {
  184.         pTrk->pSmf              = pSmf;
  185.         pTrk->tkPosition        = 0;
  186.         pTrk->cbLeft            = pTrk->smti.cbLength;
  187.         pTrk->hpbImage          = pSmf->hpbImage + pTrk->idxTrack;
  188.         pTrk->bRunningStatus    = 0;
  189.         pTrk->fdwTrack          = 0;
  190.     }
  191.  
  192.     while (SMF_SUCCESS == (smfrc = smfGetNextEvent(pSmf, (EVENT BSTACK *)&event, MAX_TICKS)))
  193.     {
  194.         if (MIDI_META == event.abEvent[0] && 
  195.             MIDI_META_TEMPO == event.abEvent[1])
  196.         {
  197.             if (3 != event.cbParm)
  198.             {
  199.                 return SMF_INVALID_FILE;
  200.             }
  201.  
  202.             if (pSmf->cTempoMap == pSmf->cTempoMapAlloc)
  203.             {
  204.                 if (NULL != pSmf->hTempoMap)
  205.                 {
  206.                     LocalUnlock(pSmf->hTempoMap);
  207.                 }
  208.                 
  209.                 pSmf->cTempoMapAlloc += C_TEMPO_MAP_CHK;
  210.                 fFirst = FALSE;
  211.                 if (0 == pSmf->cTempoMap)
  212.                 {
  213.                     hLocal = LocalAlloc(LHND, (UINT)(pSmf->cTempoMapAlloc*sizeof(TEMPOMAPENTRY)));
  214.                     fFirst = TRUE;
  215.                 }
  216.                 else
  217.                 {
  218.                     hLocal = LocalReAlloc(pSmf->hTempoMap, (UINT)(pSmf->cTempoMapAlloc*sizeof(TEMPOMAPENTRY)), LHND);
  219.                 }
  220.  
  221.                 if (NULL == hLocal)
  222.                 {
  223.                     return SMF_NO_MEMORY;
  224.                 }
  225.  
  226.                 pSmf->pTempoMap = (PTEMPOMAPENTRY)LocalLock(pSmf->hTempoMap = hLocal);
  227.             }
  228.  
  229.             if (fFirst && pSmf->tkPosition != 0)
  230.             {
  231.                 /* Inserting first event and the absolute time is zero.
  232.                 ** This is not good since we have no idea what the tempo
  233.                 ** should be; assume the standard 500,000 uSec/QN (120 BPM
  234.                 ** at 4/4 time).
  235.                 */
  236.                 
  237.                 pTempo = &pSmf->pTempoMap[pSmf->cTempoMap++];
  238.  
  239.                 pTempo->tkTempo = 0;
  240.                 pTempo->msBase  = 0;
  241.                 pTempo->dwTempo = MIDI_DEFAULT_TEMPO;
  242.  
  243.                 fFirst = FALSE;
  244.             }
  245.  
  246.             pTempo = &pSmf->pTempoMap[pSmf->cTempoMap++];
  247.  
  248.             pTempo->tkTempo = pSmf->tkPosition;
  249.             if (fFirst)
  250.                 pTempo->msBase = 0;
  251.             else
  252.             {
  253.                 /* NOTE: Better not be here unless we're q/n format!
  254.                 */
  255.                 pTempo->msBase = (pTempo-1)->msBase +
  256.                                  muldiv32(pTempo->tkTempo-((pTempo-1)->tkTempo),
  257.                                           (pTempo-1)->dwTempo,
  258.                                           1000L*pSmf->dwTimeDivision);
  259.             }
  260.             pTempo->dwTempo = (((DWORD)event.hpbParm[0])<<16)|
  261.                               (((DWORD)event.hpbParm[1])<<8)|
  262.                               ((DWORD)event.hpbParm[2]);
  263.         }
  264.     }
  265.  
  266.     if (SMF_END_OF_FILE == smfrc || SMF_SUCCESS == smfrc)
  267.     {
  268.         pSmf->tkLength = pSmf->tkPosition;
  269.         smfrc = SMF_SUCCESS;
  270.     }
  271.         
  272.     return smfrc;
  273. }
  274.  
  275. /******************************************************************************
  276. *
  277. * smfGetNextEvent
  278. *
  279. * Read the next event from the given file.
  280. *
  281. * pSmf                      - File to read the event from.
  282. *
  283. * pEvent                    - Pointer to an event structure which will receive
  284. *                             basic information about the event.
  285. *
  286. * tkMax                     - Tick destination. An attempt to read past this
  287. *                             position in the file will fail.
  288. *
  289. * Returns
  290. *   SMF_SUCCESS The events were successfully read.
  291. *   SMF_END_OF_FILE There are no more events to read in this track.
  292. *   SMF_REACHED_TKMAX No event was read because <p tkMax> was reached.
  293. *   SMF_INVALID_FILE A disk or parse error occured on the file.
  294. *
  295. * This is the lowest level of parsing for a raw MIDI stream. The basic
  296. * information about one event in the file will be returned in pEvent.
  297. *
  298. * Merging data from all tracks into one stream is performed here.
  299. * pEvent->tkDelta will contain the tick delta for the event.
  300. *
  301. * pEvent->abEvent will contain a description of the event.
  302. *  pevent->abEvent[0] will contain
  303. *    F0 or F7 for a System Exclusive message.
  304. *    FF for a MIDI file meta event.
  305. *    The status byte of any other MIDI message. (Running status will
  306. *    be tracked and expanded).
  307. *
  308. * pEvent->cbParm will contain the number of bytes of paramter data
  309. *   which is still in the file behind the event header already read.
  310. *   This data may be read with <f smfGetTrackEventData>. Any unread
  311. *   data will be skipped on the next call to <f smfGetNextTrackEvent>.
  312. *
  313. * Channel messages (0x8? - 0xE?) will always be returned fully in
  314. *   pevent->abEvent.
  315. *
  316. *  Meta events will contain the meta type in pevent->abEvent[1].
  317. *
  318. *  System exclusive events will contain only an 0xF0 or 0xF7 in
  319. *    pevent->abEvent[0].
  320. *
  321. *  The following fields in pTrk are used to maintain state and must
  322. *  be updated if a seek-in-track is performed:
  323. *
  324. *  bRunningStatus contains the last running status message or 0 if
  325. *   there is no valid running status.
  326. *
  327. *  hpbImage is a pointer into the file image of the first byte of
  328. *   the event to follow the event just read.
  329. *
  330. *  dwLeft contains the number of bytes from hpbImage to the end
  331. *   of the track.
  332. *
  333. *
  334. * Get the next due event from all (in-use?) tracks
  335. *
  336. * For all tracks
  337. *  If not end-of-track
  338. *   decode event delta time without advancing through buffer
  339. *   event_absolute_time = track_tick_time + track_event_delta_time
  340. *   relative_time = event_absolute_time - last_stream_time
  341. *   if relative_time is lowest so far
  342. *    save this track as the next to pull from, along with times
  343. *
  344. * If we found a track with a due event
  345. *  Advance track pointer past event, saving ptr to parm data if needed
  346. *  track_tick_time += track_event_delta_time
  347. *  last_stream_time = track_tick_time
  348. * Else
  349. *  Mark and return end_of_file
  350. *
  351. *****************************************************************************/
  352. SMFRESULT FNLOCAL smfGetNextEvent(
  353.     PSMF                    pSmf,
  354.     EVENT BSTACK *          pEvent,
  355.     TICKS                   tkMax)
  356. {
  357.     PTRACK                  pTrk;
  358.     PTRACK                  pTrkFound;
  359.     DWORD                   idxTrack;
  360.     TICKS                   tkEventDelta;
  361.     TICKS                   tkRelTime;
  362.     TICKS                   tkMinRelTime;
  363.     BYTE                    bEvent;
  364.     DWORD                   dwGotTotal;
  365.     DWORD                   dwGot;
  366.     DWORD                   cbEvent;
  367.  
  368.     assert(pSmf != NULL);
  369.     assert(pEvent != NULL);
  370.  
  371.     if (pSmf->fdwSMF & SMF_F_EOF)
  372.     {
  373.         return SMF_END_OF_FILE;
  374.     }
  375.  
  376.     pTrkFound       = NULL;
  377.     tkMinRelTime    = MAX_TICKS;
  378.     
  379.     for (pTrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; pTrk++)
  380.     {
  381.         if (pTrk->fdwTrack & SMF_TF_EOT)
  382.             continue;
  383.  
  384.         
  385.         if (!smfGetVDword(pTrk->hpbImage, pTrk->cbLeft, (DWORD BSTACK *)&tkEventDelta))
  386.         {
  387.             DPF(1, "Hit end of track w/o end marker!");
  388.             return SMF_INVALID_FILE;
  389.         }
  390.  
  391.         tkRelTime = pTrk->tkPosition + tkEventDelta - pSmf->tkPosition;
  392.  
  393.         if (tkRelTime < tkMinRelTime)
  394.         {
  395.             tkMinRelTime = tkRelTime;
  396.             pTrkFound = pTrk;
  397.         }
  398.     }
  399.  
  400.     if (!pTrkFound)
  401.     {
  402.         DPF(1, "END_OF_FILE!");
  403.         
  404.         pSmf->fdwSMF |= SMF_F_EOF;
  405.         return SMF_END_OF_FILE;
  406.     }
  407.  
  408.     pTrk = pTrkFound;
  409.  
  410.     if (pSmf->tkPosition + tkMinRelTime >= tkMax)
  411.     {
  412.         return SMF_REACHED_TKMAX;
  413.     }
  414.         
  415.  
  416.     pTrk->hpbImage += (dwGot = smfGetVDword(pTrk->hpbImage, pTrk->cbLeft, (DWORD BSTACK *)&tkEventDelta));
  417.     pTrk->cbLeft   -= dwGot;
  418.  
  419.     /* We MUST have at least three bytes here (cause we haven't hit
  420.     ** the end-of-track meta yet, which is three bytes long). Checking
  421.     ** against three means we don't have to check how much is left
  422.     ** in the track again for any short event, which is most cases.
  423.     */
  424.     if (pTrk->cbLeft < 3)
  425.     {
  426.         return SMF_INVALID_FILE;
  427.     }
  428.  
  429.     pTrk->tkPosition += tkEventDelta;
  430.     pEvent->tkDelta = pTrk->tkPosition - pSmf->tkPosition;
  431.     pSmf->tkPosition = pTrk->tkPosition;
  432.  
  433.     bEvent = *pTrk->hpbImage++;
  434.     
  435.     if (MIDI_MSG > bEvent)
  436.     {
  437.         if (0 == pTrk->bRunningStatus)
  438.         {
  439.             return SMF_INVALID_FILE;
  440.         }
  441.  
  442.         dwGotTotal = 1;
  443.         pEvent->abEvent[0] = pTrk->bRunningStatus;
  444.         pEvent->abEvent[1] = bEvent;
  445.         if (3 == grbChanMsgLen[(pTrk->bRunningStatus >> 4) & 0x0F])
  446.         {
  447.             pEvent->abEvent[2] = *pTrk->hpbImage++;
  448.             dwGotTotal++;
  449.         }
  450.     }
  451.     else if (MIDI_SYSEX > bEvent)
  452.     {
  453.         pTrk->bRunningStatus = bEvent;
  454.         
  455.         dwGotTotal = 2;
  456.         pEvent->abEvent[0] = bEvent;
  457.         pEvent->abEvent[1] = *pTrk->hpbImage++;
  458.         if (3 == grbChanMsgLen[(bEvent >> 4) & 0x0F])
  459.         {
  460.             pEvent->abEvent[2] = *pTrk->hpbImage++;
  461.             dwGotTotal++;
  462.         }
  463.     }
  464.     else
  465.     {
  466.         pTrk->bRunningStatus = 0;
  467.         if (MIDI_META == bEvent)
  468.         {
  469.             pEvent->abEvent[0] = MIDI_META;
  470.             if (MIDI_META_EOT == (pEvent->abEvent[1] = *pTrk->hpbImage++))
  471.             {
  472.                 pTrk->fdwTrack |= SMF_TF_EOT;
  473.             }
  474.  
  475.             dwGotTotal = 2;
  476.         }
  477.         else if (MIDI_SYSEX == bEvent || MIDI_SYSEXEND == bEvent)
  478.         {
  479.             pEvent->abEvent[0] = bEvent;
  480.             dwGotTotal = 1;
  481.         }
  482.         else
  483.         {
  484.             return SMF_INVALID_FILE;
  485.         }
  486.         
  487.         if (0 == (dwGot = smfGetVDword(pTrk->hpbImage, pTrk->cbLeft - 2, (DWORD BSTACK *)&cbEvent)))
  488.         {
  489.             return SMF_INVALID_FILE;
  490.         }
  491.  
  492.         pTrk->hpbImage  += dwGot;
  493.         dwGotTotal      += dwGot;
  494.  
  495.         if (dwGotTotal + cbEvent > pTrk->cbLeft)
  496.         {
  497.             return SMF_INVALID_FILE;
  498.         }
  499.  
  500.         pEvent->cbParm  = cbEvent;
  501.         pEvent->hpbParm = pTrk->hpbImage;
  502.  
  503.         pTrk->hpbImage += cbEvent;
  504.         dwGotTotal     += cbEvent;
  505.     }
  506.  
  507.     assert(pTrk->cbLeft >= dwGotTotal);
  508.  
  509.     pTrk->cbLeft -= dwGotTotal;
  510.  
  511.     return SMF_SUCCESS;
  512. }
  513.  
  514. /******************************************************************************
  515. *
  516. * smfGetVDword
  517. *
  518. * Reads a variable length DWORD from the given file.
  519. *
  520. * hpbImage                  - Pointer to the first byte of the VDWORD.
  521. *
  522. * dwLeft                    - Bytes left in image
  523. *
  524. * pDw                       - Pointer to a DWORD to store the result in.
  525. *                             track.
  526. *
  527. * Returns the number of bytes consumed from the stream.
  528. *
  529. * A variable length DWORD stored in a MIDI file contains one or more
  530. * bytes. Each byte except the last has the high bit set; only the
  531. * low 7 bits are significant.
  532. *  
  533. *****************************************************************************/
  534. DWORD FNLOCAL smfGetVDword(
  535.     HPBYTE                  hpbImage,                                
  536.     DWORD                   dwLeft,                               
  537.     DWORD BSTACK *          pDw)
  538. {
  539.     BYTE                    b;
  540.     DWORD                   dwUsed  = 0;
  541.  
  542.     assert(hpbImage != NULL);
  543.     assert(pDw != NULL);
  544.     
  545.     *pDw = 0;
  546.  
  547.     do
  548.     {
  549.         if (!dwLeft)
  550.         {
  551.             return 0;
  552.         }
  553.  
  554.         b = *hpbImage++;
  555.         dwLeft--;
  556.         dwUsed++;
  557.         
  558.         *pDw = (*pDw << 7) | (b & 0x7F);
  559.     } while (b&0x80);
  560.  
  561.     return dwUsed;
  562. }
  563.