SMF.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:28k
源码类别:

Windows编程

开发平台:

Visual C++

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