home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / TOOLS / MIDIPLYR / SMF.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  28.5 KB  |  898 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, 1994 Microsoft Corporation. All Rights Reserved.
  9. *
  10. ******************************************************************************
  11. *
  12. * SMF.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 SMFRESULT FNLOCAL smfInsertParmData(
  27.     PSMF                    pSmf,
  28.     TICKS                   tkDelta,                                            
  29.     LPMIDIHDR               lpmh);
  30.  
  31. /*****************************************************************************
  32. *
  33. * smfOpenFile
  34. *
  35. * This function opens a MIDI file for access. 
  36. *
  37. * psofs                     - Specifies the file to open and associated
  38. *                             parameters. Contains a valid HSMF handle
  39. *                             on success.
  40. *
  41. * Returns
  42. *   SMF_SUCCESS The specified file was opened.
  43. *
  44. *   SMF_OPEN_FAILED The specified file could not be opened because it
  45. *     did not exist or could not be created on the disk.
  46. *
  47. *   SMF_INVALID_FILE The specified file was corrupt or not a MIDI file.
  48. *   SMF_NO_MEMORY There was insufficient memory to open the file.
  49. *
  50. *   SMF_INVALID_PARM The given flags or time division in the
  51. *     SMFOPENFILESTRUCT were invalid.
  52. *****************************************************************************/
  53. SMFRESULT FNLOCAL smfOpenFile(
  54.     PSMFOPENFILESTRUCT      psofs)
  55. {
  56.     HMMIO                   hmmio = (HMMIO)NULL;
  57.     PSMF                    pSmf;
  58.     SMFRESULT               smfrc = SMF_SUCCESS;
  59.     MMIOINFO                mmioinfo;
  60.     MMCKINFO                ckRIFF;
  61.     MMCKINFO                ckDATA;
  62.  
  63.     assert(psofs != NULL);
  64.     assert(psofs->pstrName != NULL);
  65.     
  66.     /* Verify that the file can be opened or created
  67.     */
  68.     _fmemset(&mmioinfo, 0, sizeof(mmioinfo));
  69.  
  70.     hmmio = mmioOpen(psofs->pstrName, &mmioinfo, MMIO_READ|MMIO_ALLOCBUF);
  71.     if ((HMMIO)NULL == hmmio)
  72.     {
  73.         DPF(1, "smfOpenFile: mmioOpen failed!");
  74.         return SMF_OPEN_FAILED;
  75.     }
  76.  
  77.     /* Now see if we can create the handle structure
  78.     */
  79.     pSmf = (PSMF)LocalAlloc(LPTR, sizeof(SMF));
  80.     if (NULL == pSmf)
  81.     {
  82.         DPF(1, "smfOpenFile: LocalAlloc failed!");
  83.         smfrc = SMF_NO_MEMORY;
  84.         goto smf_Open_File_Cleanup;
  85.     }
  86.  
  87.     lstrcpy(pSmf->szName, psofs->pstrName);
  88.     pSmf->fdwSMF = 0;
  89.     pSmf->pTempoMap = NULL;
  90.  
  91.     /* Pull the entire file into a block of memory. 
  92.     */
  93.     _fmemset(&ckRIFF, 0, sizeof(ckRIFF));
  94.     
  95.     if (0 == mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) &&
  96.         ckRIFF.fccType == FOURCC_RMID)
  97.     {
  98.         ckDATA.ckid = FOURCC_data;
  99.         
  100.         if (0 == mmioDescend(hmmio, &ckDATA, &ckRIFF, MMIO_FINDCHUNK))
  101.         {
  102.             pSmf->cbImage   = ckDATA.cksize;
  103.         }
  104.         else
  105.         {
  106.             DPF(1, "smfOpenFile: Could not descend into RIFF DATA chunk!");
  107.             smfrc = SMF_INVALID_FILE;
  108.             goto smf_Open_File_Cleanup;
  109.         }
  110.     }
  111.     else
  112.     {
  113.         mmioSeek(hmmio, 0L, SEEK_SET);
  114.         
  115.         pSmf->cbImage = mmioSeek(hmmio, 0L, SEEK_END);
  116.         mmioSeek(hmmio, 0L, SEEK_SET);
  117.     }
  118.     
  119.     if (NULL == (pSmf->hpbImage = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, pSmf->cbImage)))
  120.     {
  121.         DPF(1, "smfOpenFile: No memory for image! [%08lX]", pSmf->cbImage);
  122.         smfrc = SMF_NO_MEMORY;
  123.         goto smf_Open_File_Cleanup;
  124.     }
  125.  
  126.     if (pSmf->cbImage != (DWORD)mmioRead(hmmio, pSmf->hpbImage, pSmf->cbImage))
  127.     {
  128.         DPF(1, "smfOpenFile: Read error on image!");
  129.         smfrc = SMF_INVALID_FILE;
  130.         goto smf_Open_File_Cleanup;
  131.     }
  132.  
  133.     /* If the file exists, parse it just enough to pull out the header and
  134.     ** build a track index.
  135.     */
  136.     smfrc = smfBuildFileIndex((PSMF BSTACK *)&pSmf);
  137.     if (MMSYSERR_NOERROR != smfrc)
  138.     {
  139.         DPF(1, "smfOpenFile: smfBuildFileIndex failed! [%lu]", (DWORD)smfrc);
  140.     }
  141.  
  142. smf_Open_File_Cleanup:
  143.  
  144.     mmioClose(hmmio, 0);
  145.  
  146.     if (SMF_SUCCESS != smfrc)
  147.     {
  148.         if (NULL != pSmf)
  149.         {
  150.             if (NULL != pSmf->hpbImage)
  151.             {
  152.                 GlobalFreePtr(pSmf->hpbImage);
  153.             }
  154.             
  155.             LocalFree((HLOCAL)pSmf);
  156.         }
  157.     }
  158.     else
  159.     {
  160.         psofs->hSmf = (HSMF)pSmf;
  161.     }
  162.     
  163.     return smfrc;
  164. }
  165.  
  166. /*****************************************************************************
  167. *
  168. * smfCloseFile
  169. *
  170. * This function closes an open MIDI file.
  171. *
  172. * hSmf                      - The handle of the open file to close.
  173. *
  174. * Returns
  175. *   SMF_SUCCESS The specified file was closed.
  176. *   SMF_INVALID_PARM The given handle was not valid.
  177. *
  178. * Any track handles opened from this file handle are invalid after this
  179. * call.
  180. *        
  181. *****************************************************************************/
  182. SMFRESULT FNLOCAL smfCloseFile(
  183.     HSMF                    hSmf)
  184. {
  185.     PSMF                    pSmf        = (PSMF)hSmf;
  186.     
  187.     assert(pSmf != NULL);
  188.     
  189.     /*
  190.     ** Free up handle memory 
  191.     */
  192.     
  193.     if (NULL != pSmf->hpbImage)
  194.         GlobalFreePtr(pSmf->hpbImage);
  195.     
  196.     LocalFree((HLOCAL)pSmf);
  197.     
  198.     return SMF_SUCCESS;
  199. }
  200.  
  201. /******************************************************************************
  202. *
  203. * smfGetFileInfo This function gets information about the MIDI file.
  204. *
  205. * hSmf                      - Specifies the open MIDI file to inquire about.
  206. *
  207. * psfi                      - A structure which will be filled in with
  208. *                             information about the file.
  209. *
  210. * Returns
  211. *   SMF_SUCCESS Information was gotten about the file.
  212. *   SMF_INVALID_PARM The given handle was invalid.
  213. *
  214. *****************************************************************************/
  215. SMFRESULT FNLOCAL smfGetFileInfo(
  216.     HSMF                    hSmf,
  217.     PSMFFILEINFO            psfi)
  218. {
  219.     PSMF                    pSmf = (PSMF)hSmf;
  220.  
  221.     assert(pSmf != NULL);
  222.     assert(psfi != NULL);
  223.  
  224.     /* 
  225.     ** Just fill in the structure with useful information.
  226.     */
  227.     psfi->dwTracks      = pSmf->dwTracks;
  228.     psfi->dwFormat      = pSmf->dwFormat;
  229.     psfi->dwTimeDivision= pSmf->dwTimeDivision;
  230.     psfi->tkLength      = pSmf->tkLength;
  231.     
  232.     return SMF_SUCCESS;
  233. }
  234.  
  235. /******************************************************************************
  236. *
  237. * smfTicksToMillisecs
  238. *
  239. * This function returns the millisecond offset into the file given the
  240. * tick offset.
  241. *
  242. * hSmf                      - Specifies the open MIDI file to perform
  243. *                             the conversion on.
  244. *
  245. * tkOffset                  - Specifies the tick offset into the stream
  246. *                             to convert.
  247. *
  248. * Returns the number of milliseconds from the start of the stream.
  249. *
  250. * The conversion is performed taking into account the file's time division and
  251. * tempo map from the first track. Note that the same millisecond value
  252. * might not be valid at a later time if the tempo track is rewritten.
  253. *
  254. *****************************************************************************/
  255. DWORD FNLOCAL smfTicksToMillisecs(
  256.     HSMF                    hSmf,
  257.     TICKS                   tkOffset)
  258. {
  259.     PSMF                    pSmf            = (PSMF)hSmf;
  260.     PTEMPOMAPENTRY          pTempo;
  261.     UINT                    idx;
  262.     UINT                    uSMPTE;
  263.     DWORD                   dwTicksPerSec;
  264.  
  265.     assert(pSmf != NULL);
  266.  
  267.     if (tkOffset > pSmf->tkLength)
  268.     {
  269.         DPF(1, "sTTM: Clipping ticks to file length!");
  270.         tkOffset = pSmf->tkLength;
  271.     }
  272.  
  273.     /* SMPTE time is easy -- no tempo map, just linear conversion
  274.     ** Note that 30-Drop means nothing to us here since we're not
  275.     ** converting to a colonized format, which is where dropping
  276.     ** happens.
  277.     */
  278.     if (pSmf->dwTimeDivision & 0x8000)
  279.     {
  280.         uSMPTE = -(int)(char)((pSmf->dwTimeDivision >> 8)&0xFF);
  281.         if (29 == uSMPTE)
  282.             uSMPTE = 30;
  283.         
  284.         dwTicksPerSec = (DWORD)uSMPTE *
  285.                         (DWORD)(BYTE)(pSmf->dwTimeDivision & 0xFF);
  286.         
  287.         return (DWORD)muldiv32(tkOffset, 1000L, dwTicksPerSec);
  288.     }
  289.        
  290.     /* Walk the tempo map and find the nearest tick position. Linearly
  291.     ** calculate the rest (using MATH.ASM)
  292.     */
  293.  
  294.     pTempo = pSmf->pTempoMap;
  295.     assert(pTempo != NULL);
  296.     
  297.     for (idx = 0; idx < pSmf->cTempoMap; idx++, pTempo++)
  298.         if (tkOffset < pTempo->tkTempo)
  299.             break;
  300.     pTempo--;
  301.  
  302.     /* pTempo is the tempo map entry preceding the requested tick offset.
  303.     */
  304.  
  305.     return pTempo->msBase + muldiv32(tkOffset-pTempo->tkTempo,
  306.                                      pTempo->dwTempo,
  307.                                      1000L*pSmf->dwTimeDivision);
  308.     
  309. }
  310.  
  311.  
  312. /******************************************************************************
  313. *
  314. * smfMillisecsToTicks
  315. *
  316. * This function returns the nearest tick offset into the file given the
  317. * millisecond offset.
  318. *
  319. * hSmf                      - Specifies the open MIDI file to perform the
  320. *                             conversion on.
  321. *
  322. * msOffset                  - Specifies the millisecond offset into the stream
  323. *                             to convert.
  324. *
  325. * Returns the number of ticks from the start of the stream.
  326. *
  327. * The conversion is performed taking into account the file's time division and
  328. * tempo map from the first track. Note that the same tick value
  329. * might not be valid at a later time if the tempo track is rewritten.
  330. * If the millisecond value does not exactly map to a tick value, then
  331. * the tick value will be rounded down.
  332. *
  333. *****************************************************************************/
  334. TICKS FNLOCAL smfMillisecsToTicks(
  335.     HSMF                    hSmf,
  336.     DWORD                   msOffset)
  337. {
  338.     PSMF                    pSmf            = (PSMF)hSmf;
  339.     PTEMPOMAPENTRY          pTempo;
  340.     UINT                    idx;
  341.     UINT                    uSMPTE;
  342.     DWORD                   dwTicksPerSec;
  343.     TICKS                   tkOffset;
  344.  
  345.     DPF(2, "smfMillisecsToTicks");
  346.     assert(pSmf != NULL);
  347.     
  348.     /* SMPTE time is easy -- no tempo map, just linear conversion
  349.     ** Note that 30-Drop means nothing to us here since we're not
  350.     ** converting to a colonized format, which is where dropping
  351.     ** happens.
  352.     */
  353.     if (pSmf->dwTimeDivision & 0x8000)
  354.     {
  355.         uSMPTE = -(int)(char)((pSmf->dwTimeDivision >> 8)&0xFF);
  356.         if (29 == uSMPTE)
  357.             uSMPTE = 30;
  358.         
  359.         dwTicksPerSec = (DWORD)uSMPTE *
  360.                         (DWORD)(BYTE)(pSmf->dwTimeDivision & 0xFF);
  361.  
  362.         DPF(2, "SMPTE: dwTicksPerSec %ld", dwTicksPerSec);
  363.         
  364.         return (DWORD)muldiv32(msOffset, dwTicksPerSec, 1000L);
  365.     }
  366.     
  367.     /* Walk the tempo map and find the nearest millisecond position. Linearly
  368.     ** calculate the rest (using MATH.ASM)
  369.     */
  370.     pTempo = pSmf->pTempoMap;
  371.     assert(pTempo != NULL);
  372.     
  373.     for (idx = 0; idx < pSmf->cTempoMap; idx++, pTempo++)
  374.         if (msOffset < pTempo->msBase)
  375.             break;
  376.     pTempo--;
  377.  
  378.     /* pTempo is the tempo map entry preceding the requested tick offset.
  379.     */
  380.  
  381.     tkOffset = pTempo->tkTempo + muldiv32(msOffset-pTempo->msBase,
  382.                                      1000L*pSmf->dwTimeDivision,
  383.                                      pTempo->dwTempo);
  384.     
  385.     if (tkOffset > pSmf->tkLength)
  386.     {
  387.         DPF(1, "sMTT: Clipping ticks to file length!");
  388.         tkOffset = pSmf->tkLength;
  389.     }
  390.  
  391.     return tkOffset;
  392. }
  393.  
  394. /******************************************************************************
  395. *
  396. * smfReadEvents
  397. *
  398. * This function reads events from a track.
  399. *
  400. * hSmf                      - Specifies the file to read data from.
  401. *
  402. * lpmh                      - Contains information about the buffer to fill.
  403. *
  404. * tkMax                     - Specifies a cutoff point in the stream
  405. *                             beyond which events will not be read.        
  406. *
  407. * Return@rdes
  408. *   SMF_SUCCESS The events were successfully read.
  409. *   SMF_END_OF_TRACK There are no more events to read in this track.
  410. *   SMF_INVALID_FILE A disk error occured on the file.
  411. * @xref <f smfWriteEvents>
  412. *****************************************************************************/
  413. SMFRESULT FNLOCAL smfReadEvents(
  414.     HSMF                    hSmf,
  415.     LPMIDIHDR               lpmh,
  416.     TICKS                   tkMax)
  417. {
  418.     PSMF                    pSmf = (PSMF)hSmf;
  419.     SMFRESULT               smfrc;
  420.     EVENT                   event;
  421.     LPDWORD                 lpdw;
  422.     DWORD                   dwTempo;
  423.  
  424.     assert(pSmf != NULL);
  425.     assert(lpmh != NULL);
  426.  
  427.     /* 
  428.     ** Read events from the track and pack them into the buffer in polymsg
  429.     ** format.
  430.     ** 
  431.     ** If a SysEx or meta would go over a buffer boundry, split it.
  432.     */ 
  433.     lpmh->dwBytesRecorded = 0;
  434.     if (pSmf->dwPendingUserEvent)
  435.     {
  436.         DPF(1, "smfReadEvents: Inserting pending event...");
  437.         
  438.         smfrc = smfInsertParmData(pSmf, (TICKS)0, lpmh);
  439.         if (SMF_SUCCESS != smfrc)
  440.         {
  441.             DPF(1, "smfInsertParmData() -> %u", (UINT)smfrc);
  442.             return smfrc;
  443.         }
  444.     }
  445.     
  446.     lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  447.  
  448.     if (pSmf->fdwSMF & SMF_F_EOF)
  449.     {
  450.         DPF(1, "smfReadEvents: SMF_F_EOF set; instant out");
  451.         return SMF_END_OF_FILE;
  452.     }
  453.  
  454.     while(TRUE)
  455.     {
  456.         assert(lpmh->dwBytesRecorded <= lpmh->dwBufferLength);
  457.         
  458.         /* If we know ahead of time we won't have room for the
  459.         ** event, just break out now. We need 2 DWORD's for the
  460.         ** terminator event and at least 2 DWORD's for any
  461.         ** event we might store - this will allow us a full
  462.         ** short event or the delta time and stub for a long
  463.         ** event to be split.
  464.         */
  465.         if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 4*sizeof(DWORD))
  466.         {
  467.             break;
  468.         }
  469.  
  470.         smfrc = smfGetNextEvent(pSmf, (SPEVENT)&event, tkMax);
  471.         if (SMF_SUCCESS != smfrc)
  472.         {
  473.             /* smfGetNextEvent doesn't set this because smfSeek uses it
  474.             ** as well and needs to distinguish between reaching the
  475.             ** seek point and reaching end-of-file.
  476.             **
  477.             ** To the user, however, we present the selection between
  478.             ** their given tkBase and tkEnd as the entire file, therefore
  479.             ** we want to translate this into EOF.
  480.             */
  481.             if (SMF_REACHED_TKMAX == smfrc)
  482.             {
  483.                 pSmf->fdwSMF |= SMF_F_EOF;
  484.             }
  485.             
  486.             DPF(1, "smfReadEvents: smfGetNextEvent() -> %u", (UINT)smfrc);
  487.             break;
  488.         }
  489.  
  490.         
  491.         if (MIDI_SYSEX > EVENT_TYPE(event))
  492.         {
  493.             *lpdw++ = (DWORD)event.tkDelta;
  494.             *lpdw++ = 0;
  495.             *lpdw++ = (((DWORD)MEVT_SHORTMSG)<<24) |
  496.                       ((DWORD)EVENT_TYPE(event)) |
  497.                       (((DWORD)EVENT_CH_B1(event)) << 8) |
  498.                       (((DWORD)EVENT_CH_B2(event)) << 16);
  499.             
  500.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  501.         }
  502.         else if (MIDI_META == EVENT_TYPE(event) &&
  503.                  MIDI_META_EOT == EVENT_META_TYPE(event))
  504.         {
  505.             /* These are ignoreable since smfReadNextEvent()
  506.             ** takes care of track merging
  507.             */
  508.             DPF(1, "smfReadEvents: Hit META_EOT");
  509.         }
  510.         else if (MIDI_META == EVENT_TYPE(event) &&
  511.                  MIDI_META_TEMPO == EVENT_META_TYPE(event))
  512.         {
  513.             if (event.cbParm != 3)
  514.             {
  515.                 DPF(1, "smfReadEvents: Corrupt tempo event");
  516.                 return SMF_INVALID_FILE;
  517.             }
  518.  
  519.             dwTempo = (((DWORD)MEVT_TEMPO)<<24)|
  520.                       (((DWORD)event.hpbParm[0])<<16)|
  521.                       (((DWORD)event.hpbParm[1])<<8)|
  522.                       ((DWORD)event.hpbParm[2]);
  523.  
  524.             *lpdw++ = (DWORD)event.tkDelta;
  525.             *lpdw++ = 0;
  526.             *lpdw++ = dwTempo;
  527.  
  528.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  529.         }
  530.         else if (MIDI_META != EVENT_TYPE(event))
  531.         {
  532.             /* Must be F0 or F7 system exclusive or FF meta
  533.             ** that we didn't recognize
  534.             */
  535.             pSmf->cbPendingUserEvent = event.cbParm;
  536.             pSmf->hpbPendingUserEvent = event.hpbParm;
  537.             pSmf->fdwSMF &= ~SMF_F_INSERTSYSEX;
  538.  
  539.             switch(EVENT_TYPE(event))
  540.             {
  541. //                case MIDI_META:
  542. //                    pSmf->dwPendingUserEvent = ((DWORD)MEVT_META) << 24;
  543. //                    break;
  544.  
  545.                 case MIDI_SYSEX:
  546.                     pSmf->fdwSMF |= SMF_F_INSERTSYSEX;
  547.             
  548.                     ++pSmf->cbPendingUserEvent;
  549.  
  550.                     /* Falling through...
  551.                     */
  552.  
  553.                 case MIDI_SYSEXEND:
  554.                     pSmf->dwPendingUserEvent = ((DWORD)MEVT_LONGMSG) << 24;
  555.                     break;
  556.             }
  557.  
  558.             smfrc = smfInsertParmData(pSmf, event.tkDelta, lpmh);
  559.             if (SMF_SUCCESS != smfrc)
  560.             {
  561.                 DPF(1, "smfInsertParmData[2] %u", (UINT)smfrc);
  562.                 return smfrc;
  563.             }
  564.  
  565.             lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  566.         }
  567.     }
  568.  
  569.     return (pSmf->fdwSMF & SMF_F_EOF) ? SMF_END_OF_FILE : SMF_SUCCESS;
  570. }
  571.  
  572. /******************************************************************************
  573. *
  574. * smfInsertParmData
  575. *
  576. * Inserts pending long data from a track into the given buffer.
  577. *
  578. * pSmf                      - Specifies the file to read data from.
  579. *
  580. * tkDelta                   - Specfices the tick delta for the data.
  581. *
  582. * lpmh                      - Contains information about the buffer to fill.
  583. *
  584. * Returns
  585. *   SMF_SUCCESS The events were successfully read.
  586. *   SMF_INVALID_FILE A disk error occured on the file.
  587. * Fills as much data as will fit while leaving room for the buffer
  588. * terminator.
  589. *
  590. * If the long data is depleted, resets pSmf->dwPendingUserEvent so
  591. * that the next event may be read.
  592. *
  593. *****************************************************************************/
  594. PRIVATE SMFRESULT FNLOCAL smfInsertParmData(
  595.     PSMF                    pSmf,
  596.     TICKS                   tkDelta,                                            
  597.     LPMIDIHDR               lpmh)
  598. {
  599.     DWORD                   dwLength;
  600.     DWORD                   dwRounded;
  601.     LPDWORD                 lpdw;
  602.  
  603.     assert(pSmf != NULL);
  604.     assert(lpmh != NULL);
  605.     
  606.     /* Can't fit 4 DWORD's? (tkDelta + stream-id + event + some data)
  607.     ** Can't do anything.
  608.     */
  609.     assert(lpmh->dwBufferLength >= lpmh->dwBytesRecorded);
  610.     
  611.     if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 4*sizeof(DWORD))
  612.     {
  613.         if (0 == tkDelta)
  614.             return SMF_SUCCESS;
  615.  
  616.         /* If we got here with a real delta, that means smfReadEvents screwed
  617.         ** up calculating left space and we should flag it somehow.
  618.         */
  619.         DPF(1, "Can't fit initial piece of SysEx into buffer!");
  620.         return SMF_INVALID_FILE;
  621.     }
  622.  
  623.     lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  624.  
  625.     dwLength = lpmh->dwBufferLength - lpmh->dwBytesRecorded - 3*sizeof(DWORD);
  626.     dwLength = min(dwLength, pSmf->cbPendingUserEvent);
  627.  
  628.     *lpdw++ = (DWORD)tkDelta;
  629.     *lpdw++ = 0L;
  630.     *lpdw++ = (pSmf->dwPendingUserEvent & 0xFF000000L) | (dwLength & 0x00FFFFFFL);
  631.  
  632.     dwRounded = (dwLength + 3) & (~3L);
  633.     
  634.     if (pSmf->fdwSMF & SMF_F_INSERTSYSEX)
  635.     {
  636.         *((LPBYTE)lpdw)++ = MIDI_SYSEX;
  637.         pSmf->fdwSMF &= ~SMF_F_INSERTSYSEX;
  638.         --dwLength;
  639.         --pSmf->cbPendingUserEvent;
  640.     }
  641.  
  642.     if (dwLength & 0x80000000L)
  643.     {
  644.         DPF(1, "dwLength %08lX  dwBytesRecorded %08lX  dwBufferLength %08lX", dwLength, lpmh->dwBytesRecorded, lpmh->dwBufferLength);
  645.         DPF(1, "cbPendingUserEvent %08lX  dwPendingUserEvent %08lX dwRounded %08lX", pSmf->cbPendingUserEvent, pSmf->dwPendingUserEvent, dwRounded);
  646.         DPF(1, "Offset into MIDI image %08lX", (DWORD)(pSmf->hpbPendingUserEvent - pSmf->hpbImage));
  647.         DPF(1, "!hmemcpy is about to fault");
  648.     }
  649.  
  650.     hmemcpy(lpdw, pSmf->hpbPendingUserEvent, dwLength);
  651.     if (0 == (pSmf->cbPendingUserEvent -= dwLength))
  652.         pSmf->dwPendingUserEvent = 0;
  653.  
  654.     lpmh->dwBytesRecorded += 3*sizeof(DWORD) + dwRounded;
  655.  
  656.     return SMF_SUCCESS;
  657. }
  658.  
  659. /******************************************************************************
  660. *
  661. * smfSeek
  662. *
  663. * This function moves the file pointer within a track
  664. * and gets the state of the track at the new position. It returns a buffer of
  665. * state information which can be used to set up to play from the new position.
  666. *
  667. * hSmf                      - Handle of file to seek within
  668. *
  669. * tkPosition                - The position to seek to in the track.
  670. *         
  671. * lpmh                      - A buffer to contain the state information.
  672. *
  673. * Returns
  674. *   SMF_SUCCESS | The state was successfully read.
  675. *   SMF_END_OF_TRACK | The pointer was moved to end of track and no state
  676. *     information was returned.
  677. *   SMF_INVALID_PARM | The given handle or buffer was invalid.
  678. *   SMF_NO_MEMORY | There was insufficient memory in the given buffer to
  679. *     contain all of the state data.
  680. *
  681. * The state information in the buffer includes patch changes, tempo changes,
  682. * time signature, key signature, 
  683. * and controller information. Only the most recent of these paramters before
  684. * the current position will be stored. The state buffer will be returned
  685. * in polymsg format so that it may be directly transmitted over the MIDI
  686. * bus to bring the state up to date.
  687. *
  688. * The buffer is mean to be sent as a streaming buffer; i.e. immediately
  689. * followed by the first data buffer. If the requested tick position
  690. * does not exist in the file, the last event in the buffer
  691. * will be a MEVT_NOP with a delta time calculated to make sure that
  692. * the next stream event plays at the proper time.
  693. *
  694. * The meta events (tempo, time signature, key signature) will be the
  695. * first events in the buffer if they exist.
  696. * Use smfGetStateMaxSize to determine the maximum size of the state
  697. * information buffer. State information that will not fit into the given
  698. * buffer will be lost.
  699. *
  700. * On return, the dwBytesRecorded field of lpmh will contain the
  701. * actual number of bytes stored in the buffer.
  702. *
  703. *****************************************************************************/
  704.  
  705. typedef struct tag_keyframe
  706. {
  707.     /*
  708.     ** Meta events. All FF's indicates never seen.
  709.     */
  710.     BYTE        rbTempo[3];
  711.  
  712.     /*
  713.     ** MIDI channel messages. FF indicates never seen.
  714.     */
  715.     BYTE        rbProgram[16];
  716.     BYTE        rbControl[16*120];
  717. }   KEYFRAME,
  718.     FAR *PKEYFRAME;
  719.  
  720. #define KF_EMPTY ((BYTE)0xFF)
  721.  
  722. SMFRESULT FNLOCAL smfSeek(
  723.     HSMF                    hSmf,
  724.     TICKS                   tkPosition,
  725.     LPMIDIHDR               lpmh)
  726. {
  727.     PSMF                    pSmf    = (PSMF)hSmf;
  728.     PTRACK                  ptrk;
  729.     DWORD                   idxTrack;
  730.     SMFRESULT               smfrc;
  731.     EVENT                   event;
  732.     LPDWORD                 lpdw;
  733.     BYTE                    bEvent;
  734.     UINT                    idx;
  735.     UINT                    idxChannel;
  736.     UINT                    idxController;
  737.     
  738.     static KEYFRAME         kf;
  739.  
  740.     _fmemset(&kf, 0xFF, sizeof(kf));
  741.     
  742.     pSmf->tkPosition = 0;
  743.     pSmf->fdwSMF &= ~SMF_F_EOF;
  744.     
  745.     for (ptrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; ptrk++)
  746.     {
  747.         ptrk->pSmf              = pSmf;
  748.         ptrk->tkPosition        = 0;
  749.         ptrk->cbLeft            = ptrk->smti.cbLength;
  750.         ptrk->hpbImage          = pSmf->hpbImage + ptrk->idxTrack;
  751.         ptrk->bRunningStatus    = 0;
  752.         ptrk->fdwTrack          = 0;
  753.     }
  754.  
  755.     while (SMF_SUCCESS == (smfrc = smfGetNextEvent(pSmf, (SPEVENT)&event, tkPosition)))
  756.     {
  757.         if (MIDI_META == (bEvent = EVENT_TYPE(event)))
  758.         {
  759.             if (EVENT_META_TYPE(event) == MIDI_META_TEMPO)
  760.             {
  761.                 if (event.cbParm != sizeof(kf.rbTempo))
  762.                     return SMF_INVALID_FILE;
  763.  
  764.                 hmemcpy((HPBYTE)kf.rbTempo, event.hpbParm, event.cbParm);
  765.             }
  766.         }
  767.         else switch(bEvent & 0xF0)
  768.         {
  769.             case MIDI_PROGRAMCHANGE:
  770.                 kf.rbProgram[bEvent & 0x0F] = EVENT_CH_B1(event);
  771.                 break;
  772.  
  773.             case MIDI_CONTROLCHANGE:
  774.                 kf.rbControl[(((WORD)bEvent & 0x0F)*120) + EVENT_CH_B1(event)] =
  775.                     EVENT_CH_B2(event);
  776.                 break;
  777.         }
  778.     }
  779.  
  780.     if (SMF_REACHED_TKMAX != smfrc)
  781.     {
  782.         return smfrc;
  783.     }
  784.  
  785.     /* Build lpmh from keyframe
  786.     */
  787.     lpmh->dwBytesRecorded = 0;
  788.     lpdw = (LPDWORD)lpmh->lpData;
  789.  
  790.     /* Tempo change event?
  791.     */
  792.     if (KF_EMPTY != kf.rbTempo[0] ||
  793.         KF_EMPTY != kf.rbTempo[1] ||
  794.         KF_EMPTY != kf.rbTempo[2])
  795.     {
  796.         if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  797.             return SMF_NO_MEMORY;
  798.  
  799.         *lpdw++ = 0;
  800.         *lpdw++ = 0;
  801.         *lpdw++ = (((DWORD)kf.rbTempo[0])<<16)|
  802.                   (((DWORD)kf.rbTempo[1])<<8)|
  803.                   ((DWORD)kf.rbTempo[2])|
  804.                   (((DWORD)MEVT_TEMPO) << 24);
  805.  
  806.         lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  807.     }
  808.  
  809.     /* Program change events?
  810.     */
  811.     for (idx = 0; idx < 16; idx++)
  812.     {
  813.         if (KF_EMPTY != kf.rbProgram[idx])
  814.         {
  815.             if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  816.                 return SMF_NO_MEMORY;
  817.  
  818.             *lpdw++ = 0;
  819.             *lpdw++ = 0;
  820.             *lpdw++ = (((DWORD)MEVT_SHORTMSG) << 24)      |
  821.                       ((DWORD)MIDI_PROGRAMCHANGE)         |
  822.                       ((DWORD)idx)                        |
  823.                       (((DWORD)kf.rbProgram[idx]) << 8);
  824.  
  825.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  826.         }
  827.     }
  828.  
  829.     /* Controller events?
  830.     */
  831.     idx = 0;
  832.     for (idxChannel = 0; idxChannel < 16; idxChannel++)
  833.     {
  834.         for (idxController = 0; idxController < 120; idxController++)
  835.         {
  836.             if (KF_EMPTY != kf.rbControl[idx])
  837.             {
  838.                 if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  839.                     return SMF_NO_MEMORY;
  840.  
  841.                 *lpdw++ = 0;
  842.                 *lpdw++ = 0;
  843.                 *lpdw++ = (((DWORD)MEVT_SHORTMSG << 24)     |
  844.                           ((DWORD)MIDI_CONTROLCHANGE)       |
  845.                           ((DWORD)idxChannel)               |
  846.                           (((DWORD)idxController) << 8)     |
  847.                           (((DWORD)kf.rbControl[idx]) << 16));
  848.  
  849.  
  850.                 lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  851.             }
  852.  
  853.             idx++;
  854.         }
  855.     }
  856.  
  857.     /* Force all tracks to be at tkPosition. We are guaranteed that
  858.     ** all tracks will be past the event immediately preceding tkPosition;
  859.     ** this will force correct delta-ticks to be generated so that events
  860.     ** on all tracks will line up properly on a seek into the middle of the
  861.     ** file.
  862.     */
  863.     for (ptrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; ptrk++)
  864.     {
  865.         ptrk->tkPosition        = tkPosition;
  866.     }
  867.     
  868.     return SMF_SUCCESS;
  869. }
  870.  
  871. /******************************************************************************
  872. *
  873. * smfGetStateMaxSize
  874. *
  875. * This function returns the maximum sizeof buffer that is needed to
  876. * hold the state information returned by f smfSeek.
  877. *
  878. * pdwSize                   - Gets the size in bytes that should be allocated
  879. *                             for the state buffer.
  880. *
  881. * Returns the state size in bytes.
  882. *
  883. *****************************************************************************/
  884. DWORD FNLOCAL smfGetStateMaxSize(
  885.     VOID)
  886. {
  887.     return  3*sizeof(DWORD) +           /* Tempo                */
  888.             3*16*sizeof(DWORD) +        /* Patch changes        */  
  889.             3*16*120*sizeof(DWORD) +    /* Controller changes   */
  890.             3*sizeof(DWORD);            /* Time alignment NOP   */
  891. }
  892.  
  893.