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 / smf.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  29KB  |  887 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. * 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.     assert(pSmf != NULL);
  346.     
  347.     /* SMPTE time is easy -- no tempo map, just linear conversion
  348.     ** Note that 30-Drop means nothing to us here since we're not
  349.     ** converting to a colonized format, which is where dropping
  350.     ** happens.
  351.     */
  352.     if (pSmf->dwTimeDivision & 0x8000)
  353.     {
  354.         uSMPTE = -(int)(char)((pSmf->dwTimeDivision >> 8)&0xFF);
  355.         if (29 == uSMPTE)
  356.             uSMPTE = 30;
  357.         
  358.         dwTicksPerSec = (DWORD)uSMPTE *
  359.                         (DWORD)(BYTE)(pSmf->dwTimeDivision & 0xFF);
  360.  
  361.         return (DWORD)muldiv32(msOffset, dwTicksPerSec, 1000L);
  362.     }
  363.     
  364.     /* Walk the tempo map and find the nearest millisecond position. Linearly
  365.     ** calculate the rest (using MATH.ASM)
  366.     */
  367.     pTempo = pSmf->pTempoMap;
  368.     assert(pTempo != NULL);
  369.     
  370.     for (idx = 0; idx < pSmf->cTempoMap; idx++, pTempo++)
  371.         if (msOffset < pTempo->msBase)
  372.             break;
  373.     pTempo--;
  374.  
  375.     /* pTempo is the tempo map entry preceding the requested tick offset.
  376.     */
  377.  
  378.     tkOffset = pTempo->tkTempo + muldiv32(msOffset-pTempo->msBase,
  379.                                      1000L*pSmf->dwTimeDivision,
  380.                                      pTempo->dwTempo);
  381.     
  382.     if (tkOffset > pSmf->tkLength)
  383.     {
  384.         DPF(1, "sMTT: Clipping ticks to file length!");
  385.         tkOffset = pSmf->tkLength;
  386.     }
  387.  
  388.     return tkOffset;
  389. }
  390.  
  391. /******************************************************************************
  392. *
  393. * smfReadEvents
  394. *
  395. * This function reads events from a track.
  396. *
  397. * hSmf                      - Specifies the file to read data from.
  398. *
  399. * lpmh                      - Contains information about the buffer to fill.
  400. *
  401. * tkMax                     - Specifies a cutoff point in the stream
  402. *                             beyond which events will not be read.        
  403. *
  404. * Return@rdes
  405. *   SMF_SUCCESS The events were successfully read.
  406. *   SMF_END_OF_TRACK There are no more events to read in this track.
  407. *   SMF_INVALID_FILE A disk error occured on the file.
  408. * @xref <f smfWriteEvents>
  409. *****************************************************************************/
  410. SMFRESULT FNLOCAL smfReadEvents(
  411.     HSMF                    hSmf,
  412.     LPMIDIHDR               lpmh,
  413.     TICKS                   tkMax)
  414. {
  415.     PSMF                    pSmf = (PSMF)hSmf;
  416.     SMFRESULT               smfrc;
  417.     EVENT                   event;
  418.     LPDWORD                 lpdw;
  419.     DWORD                   dwTempo;
  420.  
  421.     assert(pSmf != NULL);
  422.     assert(lpmh != NULL);
  423.  
  424.     /* 
  425.     ** Read events from the track and pack them into the buffer in polymsg
  426.     ** format.
  427.     ** 
  428.     ** If a SysEx or meta would go over a buffer boundry, split it.
  429.     */ 
  430.     lpmh->dwBytesRecorded = 0;
  431.     if (pSmf->dwPendingUserEvent)
  432.     {
  433.         smfrc = smfInsertParmData(pSmf, (TICKS)0, lpmh);
  434.         if (SMF_SUCCESS != smfrc)
  435.         {
  436.             DPF(1, "smfInsertParmData() -> %u", (UINT)smfrc);
  437.             return smfrc;
  438.         }
  439.     }
  440.     
  441.     lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  442.  
  443.     if (pSmf->fdwSMF & SMF_F_EOF)
  444.     {
  445.         return SMF_END_OF_FILE;
  446.     }
  447.  
  448.     while(TRUE)
  449.     {
  450.         assert(lpmh->dwBytesRecorded <= lpmh->dwBufferLength);
  451.         
  452.         /* If we know ahead of time we won't have room for the
  453.         ** event, just break out now. We need 2 DWORD's for the
  454.         ** terminator event and at least 2 DWORD's for any
  455.         ** event we might store - this will allow us a full
  456.         ** short event or the delta time and stub for a long
  457.         ** event to be split.
  458.         */
  459.         if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 4*sizeof(DWORD))
  460.         {
  461.             break;
  462.         }
  463.  
  464.         smfrc = smfGetNextEvent(pSmf, (SPEVENT)&event, tkMax);
  465.         if (SMF_SUCCESS != smfrc)
  466.         {
  467.             /* smfGetNextEvent doesn't set this because smfSeek uses it
  468.             ** as well and needs to distinguish between reaching the
  469.             ** seek point and reaching end-of-file.
  470.             **
  471.             ** To the user, however, we present the selection between
  472.             ** their given tkBase and tkEnd as the entire file, therefore
  473.             ** we want to translate this into EOF.
  474.             */
  475.             if (SMF_REACHED_TKMAX == smfrc)
  476.             {
  477.                 pSmf->fdwSMF |= SMF_F_EOF;
  478.             }
  479.             
  480.             DPF(1, "smfReadEvents: smfGetNextEvent() -> %u", (UINT)smfrc);
  481.             break;
  482.         }
  483.  
  484.         
  485.         if (MIDI_SYSEX > EVENT_TYPE(event))
  486.         {
  487.             *lpdw++ = (DWORD)event.tkDelta;
  488.             *lpdw++ = 0;
  489.             *lpdw++ = (((DWORD)MEVT_SHORTMSG)<<24) |
  490.                       ((DWORD)EVENT_TYPE(event)) |
  491.                       (((DWORD)EVENT_CH_B1(event)) << 8) |
  492.                       (((DWORD)EVENT_CH_B2(event)) << 16);
  493.             
  494.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  495.         }
  496.         else if (MIDI_META == EVENT_TYPE(event) &&
  497.                  MIDI_META_EOT == EVENT_META_TYPE(event))
  498.         {
  499.             /* These are ignoreable since smfReadNextEvent()
  500.             ** takes care of track merging
  501.             */
  502.         }
  503.         else if (MIDI_META == EVENT_TYPE(event) &&
  504.                  MIDI_META_TEMPO == EVENT_META_TYPE(event))
  505.         {
  506.             if (event.cbParm != 3)
  507.             {
  508.                 DPF(1, "smfReadEvents: Corrupt tempo event");
  509.                 return SMF_INVALID_FILE;
  510.             }
  511.  
  512.             dwTempo = (((DWORD)MEVT_TEMPO)<<24)|
  513.                       (((DWORD)event.hpbParm[0])<<16)|
  514.                       (((DWORD)event.hpbParm[1])<<8)|
  515.                       ((DWORD)event.hpbParm[2]);
  516.  
  517.             *lpdw++ = (DWORD)event.tkDelta;
  518.             *lpdw++ = 0;
  519.             *lpdw++ = dwTempo;
  520.  
  521.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  522.         }
  523.         else if (MIDI_META != EVENT_TYPE(event))
  524.         {
  525.             /* Must be F0 or F7 system exclusive or FF meta
  526.             ** that we didn't recognize
  527.             */
  528.             pSmf->cbPendingUserEvent = event.cbParm;
  529.             pSmf->hpbPendingUserEvent = event.hpbParm;
  530.             pSmf->fdwSMF &= ~SMF_F_INSERTSYSEX;
  531.  
  532.             switch(EVENT_TYPE(event))
  533.             {
  534.                 case MIDI_SYSEX:
  535.                     pSmf->fdwSMF |= SMF_F_INSERTSYSEX;
  536.             
  537.                     ++pSmf->cbPendingUserEvent;
  538.  
  539.                     /* Falling through...
  540.                     */
  541.  
  542.                 case MIDI_SYSEXEND:
  543.                     pSmf->dwPendingUserEvent = ((DWORD)MEVT_LONGMSG) << 24;
  544.                     break;
  545.             }
  546.  
  547.             smfrc = smfInsertParmData(pSmf, event.tkDelta, lpmh);
  548.             if (SMF_SUCCESS != smfrc)
  549.             {
  550.                 DPF(1, "smfInsertParmData[2] %u", (UINT)smfrc);
  551.                 return smfrc;
  552.             }
  553.  
  554.             lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  555.         }
  556.     }
  557.  
  558.     return (pSmf->fdwSMF & SMF_F_EOF) ? SMF_END_OF_FILE : SMF_SUCCESS;
  559. }
  560.  
  561. /******************************************************************************
  562. *
  563. * smfInsertParmData
  564. *
  565. * Inserts pending long data from a track into the given buffer.
  566. *
  567. * pSmf                      - Specifies the file to read data from.
  568. *
  569. * tkDelta                   - Specfices the tick delta for the data.
  570. *
  571. * lpmh                      - Contains information about the buffer to fill.
  572. *
  573. * Returns
  574. *   SMF_SUCCESS The events were successfully read.
  575. *   SMF_INVALID_FILE A disk error occured on the file.
  576. * Fills as much data as will fit while leaving room for the buffer
  577. * terminator.
  578. *
  579. * If the long data is depleted, resets pSmf->dwPendingUserEvent so
  580. * that the next event may be read.
  581. *
  582. *****************************************************************************/
  583. PRIVATE SMFRESULT FNLOCAL smfInsertParmData(
  584.     PSMF                    pSmf,
  585.     TICKS                   tkDelta,                                            
  586.     LPMIDIHDR               lpmh)
  587. {
  588.     DWORD                   dwLength;
  589.     DWORD                   dwRounded;
  590.     LPDWORD                 lpdw;
  591.  
  592.     assert(pSmf != NULL);
  593.     assert(lpmh != NULL);
  594.     
  595.     /* Can't fit 4 DWORD's? (tkDelta + stream-id + event + some data)
  596.     ** Can't do anything.
  597.     */
  598.     assert(lpmh->dwBufferLength >= lpmh->dwBytesRecorded);
  599.     
  600.     if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 4*sizeof(DWORD))
  601.     {
  602.         if (0 == tkDelta)
  603.             return SMF_SUCCESS;
  604.  
  605.         /* If we got here with a real delta, that means smfReadEvents screwed
  606.         ** up calculating left space and we should flag it somehow.
  607.         */
  608.         DPF(1, "Can't fit initial piece of SysEx into buffer!");
  609.         return SMF_INVALID_FILE;
  610.     }
  611.  
  612.     lpdw = (LPDWORD)(lpmh->lpData + lpmh->dwBytesRecorded);
  613.  
  614.     dwLength = lpmh->dwBufferLength - lpmh->dwBytesRecorded - 3*sizeof(DWORD);
  615.     dwLength = min(dwLength, pSmf->cbPendingUserEvent);
  616.  
  617.     *lpdw++ = (DWORD)tkDelta;
  618.     *lpdw++ = 0L;
  619.     *lpdw++ = (pSmf->dwPendingUserEvent & 0xFF000000L) | (dwLength & 0x00FFFFFFL);
  620.  
  621.     dwRounded = (dwLength + 3) & (~3L);
  622.     
  623.     if (pSmf->fdwSMF & SMF_F_INSERTSYSEX)
  624.     {
  625.         *((LPBYTE)lpdw)++ = MIDI_SYSEX;
  626.         pSmf->fdwSMF &= ~SMF_F_INSERTSYSEX;
  627.         --dwLength;
  628.         --pSmf->cbPendingUserEvent;
  629.     }
  630.  
  631.     if (dwLength & 0x80000000L)
  632.     {
  633.         DPF(1, "dwLength %08lX  dwBytesRecorded %08lX  dwBufferLength %08lX", dwLength, lpmh->dwBytesRecorded, lpmh->dwBufferLength);
  634.         DPF(1, "cbPendingUserEvent %08lX  dwPendingUserEvent %08lX dwRounded %08lX", pSmf->cbPendingUserEvent, pSmf->dwPendingUserEvent, dwRounded);
  635.         DPF(1, "Offset into MIDI image %08lX", (DWORD)(pSmf->hpbPendingUserEvent - pSmf->hpbImage));
  636.         DPF(1, "!hmemcpy is about to fault");
  637.     }
  638.  
  639.     hmemcpy(lpdw, pSmf->hpbPendingUserEvent, dwLength);
  640.     if (0 == (pSmf->cbPendingUserEvent -= dwLength))
  641.         pSmf->dwPendingUserEvent = 0;
  642.  
  643.     lpmh->dwBytesRecorded += 3*sizeof(DWORD) + dwRounded;
  644.  
  645.     return SMF_SUCCESS;
  646. }
  647.  
  648. /******************************************************************************
  649. *
  650. * smfSeek
  651. *
  652. * This function moves the file pointer within a track
  653. * and gets the state of the track at the new position. It returns a buffer of
  654. * state information which can be used to set up to play from the new position.
  655. *
  656. * hSmf                      - Handle of file to seek within
  657. *
  658. * tkPosition                - The position to seek to in the track.
  659. *         
  660. * lpmh                      - A buffer to contain the state information.
  661. *
  662. * Returns
  663. *   SMF_SUCCESS | The state was successfully read.
  664. *   SMF_END_OF_TRACK | The pointer was moved to end of track and no state
  665. *     information was returned.
  666. *   SMF_INVALID_PARM | The given handle or buffer was invalid.
  667. *   SMF_NO_MEMORY | There was insufficient memory in the given buffer to
  668. *     contain all of the state data.
  669. *
  670. * The state information in the buffer includes patch changes, tempo changes,
  671. * time signature, key signature, 
  672. * and controller information. Only the most recent of these paramters before
  673. * the current position will be stored. The state buffer will be returned
  674. * in polymsg format so that it may be directly transmitted over the MIDI
  675. * bus to bring the state up to date.
  676. *
  677. * The buffer is mean to be sent as a streaming buffer; i.e. immediately
  678. * followed by the first data buffer. If the requested tick position
  679. * does not exist in the file, the last event in the buffer
  680. * will be a MEVT_NOP with a delta time calculated to make sure that
  681. * the next stream event plays at the proper time.
  682. *
  683. * The meta events (tempo, time signature, key signature) will be the
  684. * first events in the buffer if they exist.
  685. * Use smfGetStateMaxSize to determine the maximum size of the state
  686. * information buffer. State information that will not fit into the given
  687. * buffer will be lost.
  688. *
  689. * On return, the dwBytesRecorded field of lpmh will contain the
  690. * actual number of bytes stored in the buffer.
  691. *
  692. *****************************************************************************/
  693.  
  694. typedef struct tag_keyframe
  695. {
  696.     /*
  697.     ** Meta events. All FF's indicates never seen.
  698.     */
  699.     BYTE        rbTempo[3];
  700.  
  701.     /*
  702.     ** MIDI channel messages. FF indicates never seen.
  703.     */
  704.     BYTE        rbProgram[16];
  705.     BYTE        rbControl[16*120];
  706. }   KEYFRAME,
  707.     FAR *PKEYFRAME;
  708.  
  709. #define KF_EMPTY ((BYTE)0xFF)
  710.  
  711. SMFRESULT FNLOCAL smfSeek(
  712.     HSMF                    hSmf,
  713.     TICKS                   tkPosition,
  714.     LPMIDIHDR               lpmh)
  715. {
  716.     PSMF                    pSmf    = (PSMF)hSmf;
  717.     PTRACK                  ptrk;
  718.     DWORD                   idxTrack;
  719.     SMFRESULT               smfrc;
  720.     EVENT                   event;
  721.     LPDWORD                 lpdw;
  722.     BYTE                    bEvent;
  723.     UINT                    idx;
  724.     UINT                    idxChannel;
  725.     UINT                    idxController;
  726.     
  727.     static KEYFRAME         kf;
  728.  
  729.     _fmemset(&kf, 0xFF, sizeof(kf));
  730.     
  731.     pSmf->tkPosition = 0;
  732.     pSmf->fdwSMF &= ~SMF_F_EOF;
  733.     
  734.     for (ptrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; ptrk++)
  735.     {
  736.         ptrk->pSmf              = pSmf;
  737.         ptrk->tkPosition        = 0;
  738.         ptrk->cbLeft            = ptrk->smti.cbLength;
  739.         ptrk->hpbImage          = pSmf->hpbImage + ptrk->idxTrack;
  740.         ptrk->bRunningStatus    = 0;
  741.         ptrk->fdwTrack          = 0;
  742.     }
  743.  
  744.     while (SMF_SUCCESS == (smfrc = smfGetNextEvent(pSmf, (SPEVENT)&event, tkPosition)))
  745.     {
  746.         if (MIDI_META == (bEvent = EVENT_TYPE(event)))
  747.         {
  748.             if (EVENT_META_TYPE(event) == MIDI_META_TEMPO)
  749.             {
  750.                 if (event.cbParm != sizeof(kf.rbTempo))
  751.                     return SMF_INVALID_FILE;
  752.  
  753.                 hmemcpy((HPBYTE)kf.rbTempo, event.hpbParm, event.cbParm);
  754.             }
  755.         }
  756.         else switch(bEvent & 0xF0)
  757.         {
  758.             case MIDI_PROGRAMCHANGE:
  759.                 kf.rbProgram[bEvent & 0x0F] = EVENT_CH_B1(event);
  760.                 break;
  761.  
  762.             case MIDI_CONTROLCHANGE:
  763.                 kf.rbControl[(((WORD)bEvent & 0x0F)*120) + EVENT_CH_B1(event)] =
  764.                     EVENT_CH_B2(event);
  765.                 break;
  766.         }
  767.     }
  768.  
  769.     if (SMF_REACHED_TKMAX != smfrc)
  770.     {
  771.         return smfrc;
  772.     }
  773.  
  774.     /* Build lpmh from keyframe
  775.     */
  776.     lpmh->dwBytesRecorded = 0;
  777.     lpdw = (LPDWORD)lpmh->lpData;
  778.  
  779.     /* Tempo change event?
  780.     */
  781.     if (KF_EMPTY != kf.rbTempo[0] ||
  782.         KF_EMPTY != kf.rbTempo[1] ||
  783.         KF_EMPTY != kf.rbTempo[2])
  784.     {
  785.         if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  786.             return SMF_NO_MEMORY;
  787.  
  788.         *lpdw++ = 0;
  789.         *lpdw++ = 0;
  790.         *lpdw++ = (((DWORD)kf.rbTempo[0])<<16)|
  791.                   (((DWORD)kf.rbTempo[1])<<8)|
  792.                   ((DWORD)kf.rbTempo[2])|
  793.                   (((DWORD)MEVT_TEMPO) << 24);
  794.  
  795.         lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  796.     }
  797.  
  798.     /* Program change events?
  799.     */
  800.     for (idx = 0; idx < 16; idx++)
  801.     {
  802.         if (KF_EMPTY != kf.rbProgram[idx])
  803.         {
  804.             if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  805.                 return SMF_NO_MEMORY;
  806.  
  807.             *lpdw++ = 0;
  808.             *lpdw++ = 0;
  809.             *lpdw++ = (((DWORD)MEVT_SHORTMSG) << 24)      |
  810.                       ((DWORD)MIDI_PROGRAMCHANGE)         |
  811.                       ((DWORD)idx)                        |
  812.                       (((DWORD)kf.rbProgram[idx]) << 8);
  813.  
  814.             lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  815.         }
  816.     }
  817.  
  818.     /* Controller events?
  819.     */
  820.     idx = 0;
  821.     for (idxChannel = 0; idxChannel < 16; idxChannel++)
  822.     {
  823.         for (idxController = 0; idxController < 120; idxController++)
  824.         {
  825.             if (KF_EMPTY != kf.rbControl[idx])
  826.             {
  827.                 if (lpmh->dwBufferLength - lpmh->dwBytesRecorded < 3*sizeof(DWORD))
  828.                     return SMF_NO_MEMORY;
  829.  
  830.                 *lpdw++ = 0;
  831.                 *lpdw++ = 0;
  832.                 *lpdw++ = (((DWORD)MEVT_SHORTMSG << 24)     |
  833.                           ((DWORD)MIDI_CONTROLCHANGE)       |
  834.                           ((DWORD)idxChannel)               |
  835.                           (((DWORD)idxController) << 8)     |
  836.                           (((DWORD)kf.rbControl[idx]) << 16));
  837.  
  838.  
  839.                 lpmh->dwBytesRecorded += 3*sizeof(DWORD);
  840.             }
  841.  
  842.             idx++;
  843.         }
  844.     }
  845.  
  846.     /* Force all tracks to be at tkPosition. We are guaranteed that
  847.     ** all tracks will be past the event immediately preceding tkPosition;
  848.     ** this will force correct delta-ticks to be generated so that events
  849.     ** on all tracks will line up properly on a seek into the middle of the
  850.     ** file.
  851.     */
  852.     for (ptrk = pSmf->rTracks, idxTrack = pSmf->dwTracks; idxTrack--; ptrk++)
  853.     {
  854.         ptrk->tkPosition        = tkPosition;
  855.     }
  856.     
  857.     return SMF_SUCCESS;
  858. }
  859.  
  860. /******************************************************************************
  861. *
  862. * smfGetStateMaxSize
  863. *
  864. * This function returns the maximum sizeof buffer that is needed to
  865. * hold the state information returned by f smfSeek.
  866. *
  867. * pdwSize                   - Gets the size in bytes that should be allocated
  868. *                             for the state buffer.
  869. *
  870. * Returns the state size in bytes.
  871. *
  872. *****************************************************************************/
  873. DWORD FNLOCAL smfGetStateMaxSize(
  874.     VOID)
  875. {
  876.     return  3*sizeof(DWORD) +           /* Tempo                */
  877.             3*16*sizeof(DWORD) +        /* Patch changes        */  
  878.             3*16*120*sizeof(DWORD) +    /* Controller changes   */
  879.             3*sizeof(DWORD);            /* Time alignment NOP   */
  880. }
  881.  
  882.