home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / audio / midiplyr / smfread.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  579 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 - 1997 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.     UNALIGNED CHUNKHDR *    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.                 ** Use defaults of 500,000 uSec/qn from MIDI spec
  233.                 */
  234.                 
  235.                 pTempo = &pSmf->pTempoMap[pSmf->cTempoMap++];
  236.  
  237.                 pTempo->tkTempo = 0;
  238.                 pTempo->msBase  = 0;
  239.                 pTempo->dwTempo = MIDI_DEFAULT_TEMPO;
  240.  
  241.                 fFirst = FALSE;
  242.             }
  243.  
  244.             pTempo = &pSmf->pTempoMap[pSmf->cTempoMap++];
  245.  
  246.             pTempo->tkTempo = pSmf->tkPosition;
  247.             if (fFirst)
  248.                 pTempo->msBase = 0;
  249.             else
  250.             {
  251.                 /* NOTE: Better not be here unless we're q/n format!
  252.                 */
  253.                 pTempo->msBase = (pTempo-1)->msBase +
  254.                                  muldiv32(pTempo->tkTempo-((pTempo-1)->tkTempo),
  255.                                           (pTempo-1)->dwTempo,
  256.                                           1000L*pSmf->dwTimeDivision);
  257.             }
  258.             pTempo->dwTempo = (((DWORD)event.hpbParm[0])<<16)|
  259.                               (((DWORD)event.hpbParm[1])<<8)|
  260.                               ((DWORD)event.hpbParm[2]);
  261.         }
  262.     }
  263.  
  264.     if (0 == pSmf->cTempoMap)
  265.     {
  266.         DPF(1, "File contains no tempo map! Insert default tempo.");
  267.  
  268.         hLocal = LocalAlloc(LHND, sizeof(TEMPOMAPENTRY));
  269.         if (!hLocal)
  270.             return SMF_NO_MEMORY;
  271.  
  272.         pSmf->pTempoMap = (PTEMPOMAPENTRY)LocalLock(pSmf->hTempoMap = hLocal);
  273.         pSmf->cTempoMap = 1;
  274.         pSmf->cTempoMapAlloc = 1;
  275.  
  276.         pSmf->pTempoMap->tkTempo = 0;
  277.         pSmf->pTempoMap->msBase  = 0;
  278.         pSmf->pTempoMap->dwTempo = MIDI_DEFAULT_TEMPO;
  279.     }
  280.  
  281.     if (SMF_END_OF_FILE == smfrc || SMF_SUCCESS == smfrc)
  282.     {
  283.         pSmf->tkLength = pSmf->tkPosition;
  284.         smfrc = SMF_SUCCESS;
  285.     }
  286.         
  287.     return smfrc;
  288. }
  289.  
  290. /******************************************************************************
  291. *
  292. * smfGetNextEvent
  293. *
  294. * Read the next event from the given file.
  295. *
  296. * pSmf                      - File to read the event from.
  297. *
  298. * pEvent                    - Pointer to an event structure which will receive
  299. *                             basic information about the event.
  300. *
  301. * tkMax                     - Tick destination. An attempt to read past this
  302. *                             position in the file will fail.
  303. *
  304. * Returns
  305. *   SMF_SUCCESS The events were successfully read.
  306. *   SMF_END_OF_FILE There are no more events to read in this track.
  307. *   SMF_REACHED_TKMAX No event was read because <p tkMax> was reached.
  308. *   SMF_INVALID_FILE A disk or parse error occured on the file.
  309. *
  310. * This is the lowest level of parsing for a raw MIDI stream. The basic
  311. * information about one event in the file will be returned in pEvent.
  312. *
  313. * Merging data from all tracks into one stream is performed here.
  314. * pEvent->tkDelta will contain the tick delta for the event.
  315. *
  316. * pEvent->abEvent will contain a description of the event.
  317. *  pevent->abEvent[0] will contain
  318. *    F0 or F7 for a System Exclusive message.
  319. *    FF for a MIDI file meta event.
  320. *    The status byte of any other MIDI message. (Running status will
  321. *    be tracked and expanded).
  322. *
  323. * pEvent->cbParm will contain the number of bytes of paramter data
  324. *   which is still in the file behind the event header already read.
  325. *   This data may be read with <f smfGetTrackEventData>. Any unread
  326. *   data will be skipped on the next call to <f smfGetNextTrackEvent>.
  327. *
  328. * Channel messages (0x8? - 0xE?) will always be returned fully in
  329. *   pevent->abEvent.
  330. *
  331. *  Meta events will contain the meta type in pevent->abEvent[1].
  332. *
  333. *  System exclusive events will contain only an 0xF0 or 0xF7 in
  334. *    pevent->abEvent[0].
  335. *
  336. *  The following fields in pTrk are used to maintain state and must
  337. *  be updated if a seek-in-track is performed:
  338. *
  339. *  bRunningStatus contains the last running status message or 0 if
  340. *   there is no valid running status.
  341. *
  342. *  hpbImage is a pointer into the file image of the first byte of
  343. *   the event to follow the event just read.
  344. *
  345. *  dwLeft contains the number of bytes from hpbImage to the end
  346. *   of the track.
  347. *
  348. *
  349. * Get the next due event from all (in-use?) tracks
  350. *
  351. * For all tracks
  352. *  If not end-of-track
  353. *   decode event delta time without advancing through buffer
  354. *   event_absolute_time = track_tick_time + track_event_delta_time
  355. *   relative_time = event_absolute_time - last_stream_time
  356. *   if relative_time is lowest so far
  357. *    save this track as the next to pull from, along with times
  358. *
  359. * If we found a track with a due event
  360. *  Advance track pointer past event, saving ptr to parm data if needed
  361. *  track_tick_time += track_event_delta_time
  362. *  last_stream_time = track_tick_time
  363. * Else
  364. *  Mark and return end_of_file
  365. *
  366. *****************************************************************************/
  367. SMFRESULT FNLOCAL smfGetNextEvent(
  368.     PSMF                    pSmf,
  369.     EVENT BSTACK *          pEvent,
  370.     TICKS                   tkMax)
  371. {
  372.     PTRACK                  pTrk;
  373.     PTRACK                  pTrkFound;
  374.     DWORD                   idxTrack;
  375.     TICKS                   tkEventDelta;
  376.     TICKS                   tkRelTime;
  377.     TICKS                   tkMinRelTime;
  378.     BYTE                    bEvent;
  379.     DWORD                   dwGotTotal;
  380.     DWORD                   dwGot;
  381.     DWORD                   cbEvent;
  382.  
  383.     assert(pSmf != NULL);
  384.     assert(pEvent != NULL);
  385.  
  386.     if (pSmf->fdwSMF & SMF_F_EOF)
  387.     {
  388.         return SMF_END_OF_FILE;
  389.     }
  390.  
  391.     pTrkFound       = NULL;
  392.     tkMinRelTime    = MAX_TICKS;
  393.     
  394.     for (pTrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; pTrk++)
  395.     {
  396.         if (pTrk->fdwTrack & SMF_TF_EOT)
  397.             continue;
  398.  
  399.         
  400.         if (!smfGetVDword(pTrk->hpbImage, pTrk->cbLeft, (DWORD BSTACK *)&tkEventDelta))
  401.         {
  402.             DPF(1, "Hit end of track w/o end marker!");
  403.             return SMF_INVALID_FILE;
  404.         }
  405.  
  406.         tkRelTime = pTrk->tkPosition + tkEventDelta - pSmf->tkPosition;
  407.  
  408.         if (tkRelTime < tkMinRelTime)
  409.         {
  410.             tkMinRelTime = tkRelTime;
  411.             pTrkFound = pTrk;
  412.         }
  413.     }
  414.  
  415.     if (!pTrkFound)
  416.     {
  417.         pSmf->fdwSMF |= SMF_F_EOF;
  418.         return SMF_END_OF_FILE;
  419.     }
  420.  
  421.     pTrk = pTrkFound;
  422.  
  423.     if (pSmf->tkPosition + tkMinRelTime >= tkMax)
  424.     {
  425.         return SMF_REACHED_TKMAX;
  426.     }
  427.         
  428.  
  429.     pTrk->hpbImage += (dwGot = smfGetVDword(pTrk->hpbImage, pTrk->cbLeft, (DWORD BSTACK *)&tkEventDelta));
  430.     pTrk->cbLeft   -= dwGot;
  431.  
  432.     /* We MUST have at least three bytes here (cause we haven't hit
  433.     ** the end-of-track meta yet, which is three bytes long). Checking
  434.     ** against three means we don't have to check how much is left
  435.     ** in the track again for any short event, which is most cases.
  436.     */
  437.     if (pTrk->cbLeft < 3)
  438.     {
  439.         return SMF_INVALID_FILE;
  440.     }
  441.  
  442.     pTrk->tkPosition += tkEventDelta;
  443.     pEvent->tkDelta = pTrk->tkPosition - pSmf->tkPosition;
  444.     pSmf->tkPosition = pTrk->tkPosition;
  445.  
  446.     bEvent = *pTrk->hpbImage++;
  447.     
  448.     if (MIDI_MSG > bEvent)
  449.     {
  450.         if (0 == pTrk->bRunningStatus)
  451.         {
  452.             return SMF_INVALID_FILE;
  453.         }
  454.  
  455.         dwGotTotal = 1;
  456.         pEvent->abEvent[0] = pTrk->bRunningStatus;
  457.         pEvent->abEvent[1] = bEvent;
  458.         if (3 == grbChanMsgLen[(pTrk->bRunningStatus >> 4) & 0x0F])
  459.         {
  460.             pEvent->abEvent[2] = *pTrk->hpbImage++;
  461.             dwGotTotal++;
  462.         }
  463.     }
  464.     else if (MIDI_SYSEX > bEvent)
  465.     {
  466.         pTrk->bRunningStatus = bEvent;
  467.         
  468.         dwGotTotal = 2;
  469.         pEvent->abEvent[0] = bEvent;
  470.         pEvent->abEvent[1] = *pTrk->hpbImage++;
  471.         if (3 == grbChanMsgLen[(bEvent >> 4) & 0x0F])
  472.         {
  473.             pEvent->abEvent[2] = *pTrk->hpbImage++;
  474.             dwGotTotal++;
  475.         }
  476.     }
  477.     else
  478.     {
  479.         pTrk->bRunningStatus = 0;
  480.         if (MIDI_META == bEvent)
  481.         {
  482.             pEvent->abEvent[0] = MIDI_META;
  483.             if (MIDI_META_EOT == (pEvent->abEvent[1] = *pTrk->hpbImage++))
  484.             {
  485.                 pTrk->fdwTrack |= SMF_TF_EOT;
  486.             }
  487.  
  488.             dwGotTotal = 2;
  489.         }
  490.         else if (MIDI_SYSEX == bEvent || MIDI_SYSEXEND == bEvent)
  491.         {
  492.             pEvent->abEvent[0] = bEvent;
  493.             dwGotTotal = 1;
  494.         }
  495.         else
  496.         {
  497.             return SMF_INVALID_FILE;
  498.         }
  499.         
  500.         if (0 == (dwGot = smfGetVDword(pTrk->hpbImage, pTrk->cbLeft - 2, (DWORD BSTACK *)&cbEvent)))
  501.         {
  502.             return SMF_INVALID_FILE;
  503.         }
  504.  
  505.         pTrk->hpbImage  += dwGot;
  506.         dwGotTotal      += dwGot;
  507.  
  508.         if (dwGotTotal + cbEvent > pTrk->cbLeft)
  509.         {
  510.             return SMF_INVALID_FILE;
  511.         }
  512.  
  513.         pEvent->cbParm  = cbEvent;
  514.         pEvent->hpbParm = pTrk->hpbImage;
  515.  
  516.         pTrk->hpbImage += cbEvent;
  517.         dwGotTotal     += cbEvent;
  518.     }
  519.  
  520.     assert(pTrk->cbLeft >= dwGotTotal);
  521.  
  522.     pTrk->cbLeft -= dwGotTotal;
  523.  
  524.     return SMF_SUCCESS;
  525. }
  526.  
  527. /******************************************************************************
  528. *
  529. * smfGetVDword
  530. *
  531. * Reads a variable length DWORD from the given file.
  532. *
  533. * hpbImage                  - Pointer to the first byte of the VDWORD.
  534. *
  535. * dwLeft                    - Bytes left in image
  536. *
  537. * pDw                       - Pointer to a DWORD to store the result in.
  538. *                             track.
  539. *
  540. * Returns the number of bytes consumed from the stream.
  541. *
  542. * A variable length DWORD stored in a MIDI file contains one or more
  543. * bytes. Each byte except the last has the high bit set; only the
  544. * low 7 bits are significant.
  545. *  
  546. *****************************************************************************/
  547. DWORD FNLOCAL smfGetVDword(
  548.     HPBYTE                  hpbImage,                                
  549.     DWORD                   dwLeft,                               
  550.     DWORD BSTACK *          pDw)
  551. {
  552.     BYTE                    b;
  553.     DWORD                   dwUsed  = 0;
  554.  
  555.     assert(hpbImage != NULL);
  556.     assert(pDw != NULL);
  557.     
  558.     *pDw = 0;
  559.  
  560.     do
  561.     {
  562.         if (!dwLeft)
  563.         {
  564.             return 0;
  565.         }
  566.  
  567.         b = *hpbImage++;
  568.         dwLeft--;
  569.         dwUsed++;
  570.         
  571.         *pDw = (*pDw << 7) | (b & 0x7F);
  572.     } while (b&0x80);
  573.  
  574.     return dwUsed;
  575. }
  576.