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 / sequence.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-05  |  26.4 KB  |  906 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. * Sequence.C
  13. *
  14. * Sequencer engine for MIDI player app
  15. *
  16. *****************************************************************************/
  17.  
  18. #include <windows.h>
  19. #include <windowsx.h>
  20. #include <mmsystem.h>
  21. #include <limits.h>
  22.  
  23. #include "debug.h"
  24. #include "seq.h"
  25.  
  26. PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
  27. PRIVATE MMRESULT FNLOCAL XlatSMFErr(SMFRESULT smfrc);
  28.  
  29. /***************************************************************************
  30. *  
  31. * seqAllocBuffers
  32. *
  33. * Allocate buffers for this instance.
  34. *
  35. * pSeq                      - The sequencer instance to allocate buffers for.
  36. *
  37. * Returns
  38. *   MMSYSERR_NOERROR If the operation was successful.
  39. *
  40. *   MCIERR_OUT_OF_MEMORY  If there is insufficient memory for
  41. *     the requested number and size of buffers.
  42. *
  43. * seqAllocBuffers allocates playback buffers based on the
  44. * cbBuffer and cBuffer fields of pSeq. cbBuffer specifies the
  45. * number of bytes in each buffer, and cBuffer specifies the
  46. * number of buffers to allocate.
  47. *
  48. * seqAllocBuffers must be called before any other sequencer call
  49. * on a newly allocted SEQUENCE structure. It must be paired with
  50. * a call to seqFreeBuffers, which should be the last call made
  51. * before the SEQUENCE structure is discarded.
  52. *
  53. ***************************************************************************/
  54. MMRESULT FNLOCAL seqAllocBuffers(
  55.     PSEQ                    pSeq)
  56. {
  57.     DWORD                   dwEachBufferSize;
  58.     DWORD                   dwAlloc;
  59.     UINT                    i;
  60.     LPBYTE                  lpbWork;
  61.  
  62.     assert(pSeq != NULL);
  63.  
  64.     pSeq->uState    = SEQ_S_NOFILE;
  65.     pSeq->lpmhFree  = NULL;
  66.     pSeq->lpbAlloc  = NULL;
  67.     pSeq->hSmf      = (HSMF)NULL;
  68.     
  69.     /* First make sure we can allocate the buffers they asked for
  70.     */
  71.     dwEachBufferSize = sizeof(MIDIHDR) + (DWORD)(pSeq->cbBuffer);
  72.     dwAlloc          = dwEachBufferSize * (DWORD)(pSeq->cBuffer);
  73.     
  74.     pSeq->lpbAlloc = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, dwAlloc);
  75.     if (NULL == pSeq->lpbAlloc)
  76.         return MCIERR_OUT_OF_MEMORY;
  77.  
  78.     /* Initialize all MIDIHDR's and throw them into a free list
  79.     */
  80.     pSeq->lpmhFree = NULL;
  81.  
  82.     lpbWork = pSeq->lpbAlloc;
  83.     for (i=0; i < pSeq->cBuffer; i++)
  84.     {
  85.         ((LPMIDIHDR)lpbWork)->lpNext            = pSeq->lpmhFree;
  86.  
  87.         ((LPMIDIHDR)lpbWork)->lpData            = lpbWork + sizeof(MIDIHDR);
  88.         ((LPMIDIHDR)lpbWork)->dwBufferLength    = pSeq->cbBuffer;
  89.         ((LPMIDIHDR)lpbWork)->dwBytesRecorded   = 0;
  90.         ((LPMIDIHDR)lpbWork)->dwUser            = (DWORD)(UINT)pSeq;
  91.         ((LPMIDIHDR)lpbWork)->dwFlags           = 0;
  92.  
  93.         pSeq->lpmhFree = (LPMIDIHDR)lpbWork;
  94.  
  95.         lpbWork += dwEachBufferSize;
  96.     }
  97.  
  98.     return MMSYSERR_NOERROR;
  99. }
  100.  
  101. /***************************************************************************
  102. *  
  103. * seqFreeBuffers
  104. *
  105. * Free buffers for this instance.
  106. *
  107. * pSeq                      - The sequencer instance to free buffers for.
  108. *   
  109. * seqFreeBuffers frees all allocated memory belonging to the
  110. * given sequencer instance pSeq. It must be the last call
  111. * performed on the instance before it is destroyed.
  112. *       
  113. ****************************************************************************/
  114. VOID FNLOCAL seqFreeBuffers(
  115.     PSEQ                    pSeq)
  116. {
  117.     LPMIDIHDR               lpmh;
  118.     
  119.     assert(pSeq != NULL);
  120.  
  121.     if (NULL != pSeq->lpbAlloc)
  122.     {
  123.         lpmh = (LPMIDIHDR)pSeq->lpbAlloc;
  124.         assert(!(lpmh->dwFlags & MHDR_PREPARED));
  125.         
  126.         GlobalFreePtr(pSeq->lpbAlloc);
  127.     }
  128. }
  129.  
  130. /***************************************************************************
  131. *  
  132. * seqOpenFile
  133. *
  134. * Associates a MIDI file with the given sequencer instance.
  135. *
  136. * pSeq                      - The sequencer instance.
  137. *
  138. * Returns
  139. *   MMSYSERR_NOERROR If the operation is successful.
  140. *    
  141. *   MCIERR_UNSUPPORTED_FUNCTION If there is already a file open
  142. *     on this instance.
  143. *     
  144. *   MCIERR_OUT_OF_MEMORY If there was insufficient memory to
  145. *     allocate internal buffers on the file.
  146. *
  147. *   MCIERR_INVALID_FILE If initial attempts to parse the file
  148. *     failed (such as the file is not a MIDI or RMI file).
  149. *
  150. * seqOpenFile may only be called if there is no currently open file
  151. * on the instance. It must be paired with a call to seqCloseFile
  152. * when operations on this file are complete.
  153. *
  154. * The pstrFile field of pSeq contains the name of the file
  155. * to open. This name will be passed directly to mmioOpen; it may
  156. * contain a specifcation for a custom MMIO file handler. The task
  157. * context used for all I/O will be the task which calls seqOpenFile.
  158. *
  159. ***************************************************************************/
  160. MMRESULT FNLOCAL seqOpenFile(
  161.     PSEQ                    pSeq)
  162. {                            
  163.     MMRESULT                rc      = MMSYSERR_NOERROR;
  164.     SMFOPENFILESTRUCT       sofs;
  165.     SMFFILEINFO             sfi;
  166.     SMFRESULT               smfrc;
  167.     DWORD                   cbBuffer;
  168.  
  169.     assert(pSeq != NULL);
  170.  
  171.     if (pSeq->uState != SEQ_S_NOFILE)
  172.     {
  173.         return MCIERR_UNSUPPORTED_FUNCTION;
  174.     }
  175.  
  176.     assert(pSeq->pstrFile != NULL);
  177.     
  178.     sofs.pstrName     = pSeq->pstrFile;
  179.  
  180.     smfrc = smfOpenFile(&sofs);
  181.     if (SMF_SUCCESS != smfrc)
  182.     {
  183.         rc = XlatSMFErr(smfrc);
  184.         goto Seq_Open_File_Cleanup;
  185.     }
  186.  
  187.     pSeq->hSmf = sofs.hSmf;
  188.     smfGetFileInfo(pSeq->hSmf, &sfi);
  189.     
  190.     pSeq->dwTimeDivision = sfi.dwTimeDivision;
  191.     pSeq->tkLength       = sfi.tkLength;
  192.     pSeq->cTrk           = sfi.dwTracks;
  193.                
  194.     /* Track buffers must be big enough to hold the state data returned
  195.     ** by smfSeek()
  196.     */
  197.     cbBuffer = min(pSeq->cbBuffer, smfGetStateMaxSize());
  198.     
  199. Seq_Open_File_Cleanup:    
  200.     if (MMSYSERR_NOERROR != rc)
  201.         seqCloseFile(pSeq);
  202.     else
  203.         pSeq->uState = SEQ_S_OPENED;
  204.  
  205.     return rc;
  206. }
  207.  
  208. /***************************************************************************
  209. *  
  210. * seqCloseFile
  211. *
  212. * Deassociates a MIDI file with the given sequencer instance.
  213. *
  214. * pSeq                      -  The sequencer instance.
  215. *
  216. * Returns
  217. *   MMSYSERR_NOERROR If the operation is successful.
  218. *    
  219. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  220. *     stopped.
  221. *     
  222. * A call to seqCloseFile must be paired with a prior call to
  223. * seqOpenFile. All buffers associated with the file will be
  224. * freed and the file will be closed. The sequencer must be
  225. * stopped before this call will be accepted.
  226. *
  227. ***************************************************************************/
  228. MMRESULT FNLOCAL seqCloseFile(
  229.     PSEQ                    pSeq)
  230. {
  231.     LPMIDIHDR               lpmh;
  232.     
  233.     assert(pSeq != NULL);
  234.     
  235.     if (SEQ_S_OPENED != pSeq->uState)
  236.         return MCIERR_UNSUPPORTED_FUNCTION;
  237.     
  238.     if ((HSMF)NULL != pSeq->hSmf)
  239.     {
  240.         smfCloseFile(pSeq->hSmf);
  241.         pSeq->hSmf = (HSMF)NULL;
  242.     }
  243.  
  244.     /* If we were prerolled, need to clean up -- have an open MIDI handle
  245.     ** and buffers in the ready queue
  246.     */
  247.  
  248.     for (lpmh = pSeq->lpmhFree; lpmh; lpmh = lpmh->lpNext)
  249.         midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh));
  250.  
  251.     if (pSeq->lpmhPreroll)
  252.         midiOutUnprepareHeader(pSeq->hmidi, pSeq->lpmhPreroll, sizeof(*pSeq->lpmhPreroll));
  253.  
  254.     if (pSeq->hmidi != NULL)
  255.     {
  256.         midiStreamClose(pSeq->hmidi);
  257.         pSeq->hmidi = NULL;
  258.     }
  259.  
  260.     pSeq->uState = SEQ_S_NOFILE;
  261.  
  262.     return MMSYSERR_NOERROR;
  263. }
  264.  
  265. /***************************************************************************
  266. *  
  267. * seqPreroll
  268. *
  269. * Prepares the file for playback at the given position.
  270. *
  271. * pSeq                      - The sequencer instance.
  272. *
  273. * lpPreroll                 - Specifies the starting and ending tick
  274. *                             positions to play between.
  275. *
  276. * Returns
  277. *   MMSYSERR_NOERROR If the operation is successful.
  278. *    
  279. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  280. *     opened or prerolled.
  281. *
  282. * Open the device so we can initialize channels.
  283. *
  284. * Loop through the tracks. For each track, seek to the given position and
  285. * send the init data SMF gives us to the handle.
  286. *
  287. * Wait for all init buffers to finish.
  288. *
  289. * Unprepare the buffers (they're only ever sent here; the sequencer
  290. * engine merges them into a single stream during normal playback) and
  291. * refill them with the first chunk of data from the track. 
  292. *
  293. *     
  294. ****************************************************************************/
  295. MMRESULT FNLOCAL seqPreroll(
  296.     PSEQ                    pSeq,
  297.     LPPREROLL               lpPreroll)
  298. {
  299.     SMFRESULT           smfrc;
  300.     MMRESULT            mmrc        = MMSYSERR_NOERROR;
  301.     MIDIPROPTIMEDIV     mptd;
  302.     LPMIDIHDR           lpmh = NULL;
  303.     LPMIDIHDR           lpmhPreroll = NULL;
  304.     DWORD               cbPrerollBuffer;
  305.     UINT                uDeviceID;
  306.  
  307.     assert(pSeq != NULL);
  308.  
  309.     pSeq->mmrcLastErr = MMSYSERR_NOERROR;
  310.  
  311.     if (pSeq->uState != SEQ_S_OPENED &&
  312.         pSeq->uState != SEQ_S_PREROLLED)
  313.         return MCIERR_UNSUPPORTED_FUNCTION;
  314.  
  315.     pSeq->tkBase = lpPreroll->tkBase;
  316.     pSeq->tkEnd = lpPreroll->tkEnd;
  317.  
  318.     if (pSeq->hmidi)
  319.     {
  320.         // Recollect buffers from MMSYSTEM back into free queue
  321.         //
  322.         pSeq->uState = SEQ_S_RESET;
  323.         midiOutReset(pSeq->hmidi);
  324.  
  325.         while (pSeq->uBuffersInMMSYSTEM)
  326.             Sleep(0);
  327.     }
  328.     
  329.     pSeq->uBuffersInMMSYSTEM = 0;
  330.     pSeq->uState = SEQ_S_PREROLLING;
  331.     
  332.     //
  333.     // We've successfully opened the file and all of the tracks; now
  334.     // open the MIDI device and set the time division.
  335.     //
  336.     // NOTE: seqPreroll is equivalent to seek; device might already be open
  337.     //
  338.     if (NULL == pSeq->hmidi)
  339.     {
  340.         uDeviceID = pSeq->uDeviceID;
  341.         if ((mmrc = midiStreamOpen(&pSeq->hmidi,
  342.                                    &uDeviceID,
  343.                                    1,
  344.                                    (DWORD)seqMIDICallback,
  345.                                    0,
  346.                                    CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
  347.         {
  348.             pSeq->hmidi = NULL;
  349.             goto seq_Preroll_Cleanup;
  350.         }
  351.         
  352.         mptd.cbStruct  = sizeof(mptd);
  353.         mptd.dwTimeDiv = pSeq->dwTimeDivision;
  354.         if ((mmrc = midiStreamProperty(
  355.                                        (HMIDI)pSeq->hmidi,
  356.                                        (LPBYTE)&mptd,
  357.                                        MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR)
  358.         {
  359.             DPF(1, "midiStreamProperty() -> %04X", (WORD)mmrc);
  360.             midiStreamClose(pSeq->hmidi);
  361.             pSeq->hmidi = NULL;
  362.             mmrc = MCIERR_DEVICE_NOT_READY;
  363.             goto seq_Preroll_Cleanup;
  364.         }
  365.     }
  366.  
  367.     mmrc = MMSYSERR_NOERROR;
  368.  
  369.     //
  370.     //  Allocate a preroll buffer.  Then if we don't have enough room for
  371.     //  all the preroll info, we make the buffer larger.  
  372.     //
  373.     if (!pSeq->lpmhPreroll)
  374.     {
  375.         cbPrerollBuffer = 4096;
  376.         lpmhPreroll = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE,
  377.                                                             cbPrerollBuffer);
  378.     }
  379.     else
  380.     {
  381.         cbPrerollBuffer = pSeq->cbPreroll;
  382.         lpmhPreroll = pSeq->lpmhPreroll;
  383.     }
  384.  
  385.     lpmhPreroll->lpNext            = pSeq->lpmhFree;
  386.     lpmhPreroll->lpData            = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
  387.     lpmhPreroll->dwBufferLength    = cbPrerollBuffer - sizeof(MIDIHDR);
  388.     lpmhPreroll->dwBytesRecorded   = 0;
  389.     lpmhPreroll->dwUser            = (DWORD)(UINT)pSeq;
  390.     lpmhPreroll->dwFlags           = 0;
  391.  
  392.     do
  393.     {
  394.         smfrc = smfSeek(pSeq->hSmf, pSeq->tkBase, lpmhPreroll);
  395.         if( SMF_SUCCESS != smfrc )
  396.         {
  397.             if( ( SMF_NO_MEMORY != smfrc )  ||
  398.                 ( cbPrerollBuffer >= 32768L ) )
  399.             {
  400.                 DPF(1, "smfSeek() returned %lu", (DWORD)smfrc);
  401.  
  402.                 GlobalFreePtr(lpmhPreroll);
  403.                 pSeq->lpmhPreroll = NULL;
  404.  
  405.                 mmrc = XlatSMFErr(smfrc);
  406.                 goto seq_Preroll_Cleanup;
  407.             }
  408.             else   //  Try to grow buffer.
  409.             {
  410.                 cbPrerollBuffer *= 2;
  411.                 lpmh = (LPMIDIHDR)GlobalReAllocPtr( lpmhPreroll, cbPrerollBuffer, 0 );
  412.                 if( NULL == lpmh )
  413.                 {
  414.                     DPF(2,"seqPreroll - realloc failed, aborting preroll.");
  415.                     mmrc = MCIERR_OUT_OF_MEMORY;
  416.                     goto seq_Preroll_Cleanup;
  417.                 }
  418.  
  419.                 lpmhPreroll = lpmh;
  420.                 lpmhPreroll->lpData = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
  421.                 lpmhPreroll->dwBufferLength = cbPrerollBuffer - sizeof(MIDIHDR);
  422.  
  423.                 pSeq->lpmhPreroll = lpmhPreroll;
  424.                 pSeq->cbPreroll = cbPrerollBuffer;
  425.             }
  426.         }
  427.     } while( SMF_SUCCESS != smfrc );
  428.  
  429.     if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
  430.     {
  431.         DPF(1, "midiOutPrepare(preroll) -> %lu!", (DWORD)mmrc);
  432.  
  433.         mmrc = MCIERR_DEVICE_NOT_READY;
  434.         goto seq_Preroll_Cleanup;
  435.     }
  436.  
  437.     ++pSeq->uBuffersInMMSYSTEM;
  438.  
  439.     if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
  440.     {
  441.         DPF(1, "midiStreamOut(preroll) -> %lu!", (DWORD)mmrc);
  442.  
  443.         mmrc = MCIERR_DEVICE_NOT_READY;
  444.         --pSeq->uBuffersInMMSYSTEM;
  445.         goto seq_Preroll_Cleanup;
  446.     }
  447.     DPF(3,"seqPreroll: midiStreamOut(0x%x,0x%lx,%u) returned %u.",pSeq->hmidi,lpmhPreroll,sizeof(MIDIHDR),mmrc);
  448.  
  449.     pSeq->fdwSeq &= ~SEQ_F_EOF;
  450.     while (pSeq->lpmhFree)
  451.     {
  452.         lpmh = pSeq->lpmhFree;
  453.         pSeq->lpmhFree = lpmh->lpNext;
  454.  
  455.         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
  456.         if (SMF_SUCCESS != smfrc && SMF_END_OF_FILE != smfrc)
  457.         {
  458.             DPF(1, "SFP: smfReadEvents() -> %u", (UINT)smfrc);
  459.             mmrc = XlatSMFErr(smfrc);
  460.             goto seq_Preroll_Cleanup;
  461.         }
  462.  
  463.         if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  464.         {
  465.             DPF(1, "SFP: midiOutPrepareHeader failed");
  466.             goto seq_Preroll_Cleanup;
  467.         }
  468.  
  469.         if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  470.         {
  471.             DPF(1, "SFP: midiStreamOut failed");
  472.             goto seq_Preroll_Cleanup;
  473.         }
  474.  
  475.         ++pSeq->uBuffersInMMSYSTEM; 
  476.  
  477.         if (SMF_END_OF_FILE == smfrc)
  478.         {
  479.             pSeq->fdwSeq |= SEQ_F_EOF;
  480.             break;
  481.         }
  482.     } 
  483.  
  484. seq_Preroll_Cleanup:
  485.     if (MMSYSERR_NOERROR != mmrc)
  486.     {
  487.         pSeq->uState = SEQ_S_OPENED;
  488.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  489.     }
  490.     else
  491.     {
  492.         pSeq->uState = SEQ_S_PREROLLED;
  493.     }
  494.  
  495.     return mmrc;
  496. }
  497.  
  498. /***************************************************************************
  499. *  
  500. * seqStart
  501. *
  502. * Starts playback at the current position.
  503. *
  504. * pSeq                      - The sequencer instance.
  505. *
  506. * Returns
  507. *   MMSYSERR_NOERROR If the operation is successful.
  508. *    
  509. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  510. *     stopped.
  511. *
  512. *   MCIERR_DEVICE_NOT_READY If the underlying MIDI device could
  513. *     not be opened or fails any call.
  514. * The sequencer must be prerolled before seqStart may be called.
  515. *
  516. * Just feed everything in the ready queue to the device.
  517. *       
  518. ***************************************************************************/
  519. MMRESULT FNLOCAL seqStart(
  520.     PSEQ                    pSeq)
  521. {
  522.     assert(NULL != pSeq);
  523.  
  524.     if (SEQ_S_PREROLLED != pSeq->uState)
  525.     {
  526.         DPF(1, "seqStart(): State is wrong! [%u]", pSeq->uState);
  527.         return MCIERR_UNSUPPORTED_FUNCTION;
  528.     }
  529.  
  530.     pSeq->uState = SEQ_S_PLAYING;
  531.  
  532.     return midiStreamRestart(pSeq->hmidi);
  533. }
  534.  
  535. /***************************************************************************
  536. *  
  537. * seqPause
  538. *
  539. * Pauses playback of the instance.
  540. *
  541. * pSeq                      - The sequencer instance.
  542. *
  543. * Returns
  544. *   MMSYSERR_NOERROR If the operation is successful.
  545. *    
  546. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  547. *     playing.
  548. *
  549. * The sequencer must be playing before seqPause may be called.
  550. * Pausing the sequencer will cause all currently on notes to be turned
  551. * off. This may cause playback to be slightly inaccurate on restart
  552. * due to missing notes.
  553. *       
  554. ***************************************************************************/
  555. MMRESULT FNLOCAL seqPause(
  556.     PSEQ                    pSeq)
  557. {
  558.     assert(NULL != pSeq);
  559.     
  560.     if (SEQ_S_PLAYING != pSeq->uState)
  561.         return MCIERR_UNSUPPORTED_FUNCTION;
  562.  
  563.     pSeq->uState = SEQ_S_PAUSED;
  564.     midiStreamPause(pSeq->hmidi);
  565.     
  566.     return MMSYSERR_NOERROR;
  567. }
  568.  
  569. /***************************************************************************
  570. *  
  571. * seqRestart
  572. *
  573. * Restarts playback of an instance after a pause.
  574. *
  575. * pSeq                      - The sequencer instance.
  576. *
  577. * Returns
  578. *    MMSYSERR_NOERROR If the operation is successful.
  579. *    
  580. *    MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  581. *     paused.
  582. *
  583. * The sequencer must be paused before seqRestart may be called.
  584. *
  585. ***************************************************************************/
  586. MMRESULT FNLOCAL seqRestart(
  587.     PSEQ                    pSeq)
  588. {
  589.     assert(NULL != pSeq);
  590.  
  591.     if (SEQ_S_PAUSED != pSeq->uState)
  592.         return MCIERR_UNSUPPORTED_FUNCTION;
  593.  
  594.     pSeq->uState = SEQ_S_PLAYING;
  595.     midiStreamRestart(pSeq->hmidi);
  596.  
  597.     return MMSYSERR_NOERROR;
  598. }
  599.  
  600. /***************************************************************************
  601. *  
  602. * seqStop
  603. *
  604. * Totally stops playback of an instance.
  605. *
  606. * pSeq                      - The sequencer instance.
  607. *
  608. * Returns
  609. *   MMSYSERR_NOERROR If the operation is successful.
  610. *    
  611. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  612. *     paused or playing.
  613. *
  614. * The sequencer must be paused or playing before seqStop may be called.
  615. *
  616. ***************************************************************************/
  617. MMRESULT FNLOCAL seqStop(
  618.     PSEQ                    pSeq)
  619. {
  620.     assert(NULL != pSeq);
  621.  
  622.     /* Automatic success if we're already stopped
  623.     */
  624.     if (SEQ_S_PLAYING != pSeq->uState &&
  625.         SEQ_S_PAUSED != pSeq->uState)
  626.     {
  627.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  628.         return MMSYSERR_NOERROR;
  629.     }
  630.  
  631.     pSeq->uState = SEQ_S_STOPPING;
  632.     pSeq->fdwSeq |= SEQ_F_WAITING;
  633.     
  634.     if (MMSYSERR_NOERROR != (pSeq->mmrcLastErr = midiStreamStop(pSeq->hmidi)))
  635.     {
  636.         DPF(1, "midiOutStop() returned %lu in seqStop()!", (DWORD)pSeq->mmrcLastErr);
  637.         
  638.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  639.         return MCIERR_DEVICE_NOT_READY;
  640.     }
  641.  
  642.     while (pSeq->uBuffersInMMSYSTEM)
  643.         Sleep(0);
  644.     
  645.     return MMSYSERR_NOERROR;
  646. }
  647.  
  648. /***************************************************************************
  649. *  
  650. * seqTime
  651. *
  652. * Determine the current position in playback of an instance.
  653. *
  654. * pSeq                      - The sequencer instance.
  655. *
  656. * pTicks                    - A pointer to a DWORD where the current position
  657. *                             in ticks will be returned.
  658. *
  659. * Returns
  660. *   MMSYSERR_NOERROR If the operation is successful.
  661. *
  662. *   MCIERR_DEVICE_NOT_READY If the underlying device fails to report
  663. *     the position.
  664. *    
  665. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  666. *     paused or playing.
  667. *
  668. * The sequencer must be paused, playing or prerolled before seqTime
  669. * may be called.
  670. *
  671. ***************************************************************************/
  672. MMRESULT FNLOCAL seqTime(
  673.     PSEQ                    pSeq,
  674.     PTICKS                  pTicks)
  675. {
  676.     MMRESULT                mmr;
  677.     MMTIME                  mmt;
  678.     
  679.     assert(pSeq != NULL);
  680.  
  681.     if (SEQ_S_PLAYING != pSeq->uState &&
  682.         SEQ_S_PAUSED != pSeq->uState &&
  683.         SEQ_S_PREROLLING != pSeq->uState &&
  684.         SEQ_S_PREROLLED != pSeq->uState &&
  685.         SEQ_S_OPENED != pSeq->uState)
  686.     {
  687.         DPF(1, "seqTime(): State wrong! [is %u]", pSeq->uState);
  688.         return MCIERR_UNSUPPORTED_FUNCTION;
  689.     }
  690.  
  691.     *pTicks = 0;
  692.     if (SEQ_S_OPENED != pSeq->uState)
  693.     {
  694.         *pTicks = pSeq->tkBase;
  695.         if (SEQ_S_PREROLLED != pSeq->uState)
  696.         {
  697.             mmt.wType = TIME_TICKS;
  698.             mmr = midiStreamPosition(pSeq->hmidi, &mmt, sizeof(mmt));
  699.             if (MMSYSERR_NOERROR != mmr)
  700.             {
  701.                 DPF(1, "midiStreamPosition() returned %lu", (DWORD)mmr);
  702.                 return MCIERR_DEVICE_NOT_READY;
  703.             }
  704.  
  705.             *pTicks += mmt.u.ticks;
  706.         }
  707.     }
  708.  
  709.     return MMSYSERR_NOERROR;
  710. }
  711.                               
  712. /***************************************************************************
  713. *  
  714. * seqMillisecsToTicks
  715. *
  716. * Given a millisecond offset in the output stream, returns the associated
  717. * tick position.
  718. *
  719. * pSeq                      - The sequencer instance.
  720. *
  721. * msOffset                  - The millisecond offset into the stream.
  722. *
  723. * Returns the number of ticks into the stream.
  724. *
  725. ***************************************************************************/
  726. TICKS FNLOCAL seqMillisecsToTicks(
  727.     PSEQ                    pSeq,
  728.     DWORD                   msOffset)
  729. {
  730.     return smfMillisecsToTicks(pSeq->hSmf, msOffset);
  731. }
  732.  
  733. /***************************************************************************
  734. *  
  735. * seqTicksToMillisecs
  736. *
  737. * Given a tick offset in the output stream, returns the associated
  738. * millisecond position.
  739. *
  740. * pSeq                      - The sequencer instance.
  741. *
  742. * tkOffset                  - The tick offset into the stream.
  743. *
  744. * Returns the number of milliseconds into the stream.
  745. *
  746. ***************************************************************************/
  747. DWORD FNLOCAL seqTicksToMillisecs(
  748.     PSEQ                    pSeq,
  749.     TICKS                   tkOffset)
  750. {
  751.     return smfTicksToMillisecs(pSeq->hSmf, tkOffset);
  752. }
  753.  
  754. /***************************************************************************
  755. *  
  756. * seqMIDICallback
  757. *
  758. * Called by the system when a buffer is done
  759. *
  760. * dw1                       - The buffer that has completed playback.
  761. *
  762. ***************************************************************************/
  763. PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
  764. {
  765.     LPMIDIHDR                    lpmh        = (LPMIDIHDR)dw1;
  766.     PSEQ                    pSeq;
  767.     MMRESULT                mmrc;
  768.     SMFRESULT               smfrc;
  769.  
  770.     if (uMsg != MOM_DONE)
  771.         return;
  772.  
  773.     assert(NULL != lpmh);
  774.  
  775.     pSeq = (PSEQ)(lpmh->dwUser);
  776.  
  777.     assert(pSeq != NULL);
  778.  
  779.     --pSeq->uBuffersInMMSYSTEM;
  780.     
  781.     if (SEQ_S_RESET == pSeq->uState)
  782.     {
  783.         // We're recollecting buffers from MMSYSTEM
  784.         //
  785.         if (lpmh != pSeq->lpmhPreroll)
  786.         {
  787.             lpmh->lpNext   = pSeq->lpmhFree;
  788.             pSeq->lpmhFree = lpmh;
  789.         }
  790.  
  791.         return;
  792.     }
  793.     
  794.  
  795.     if ((SEQ_S_STOPPING == pSeq->uState) || (pSeq->fdwSeq & SEQ_F_EOF))
  796.     {
  797.         /*
  798.         ** Reached EOF, just put the buffer back on the free
  799.         ** list 
  800.         */
  801.         if (lpmh != pSeq->lpmhPreroll)
  802.         {
  803.             lpmh->lpNext   = pSeq->lpmhFree;
  804.             pSeq->lpmhFree = lpmh;
  805.         }
  806.  
  807.         if (MMSYSERR_NOERROR != (mmrc = midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  808.         {
  809.             DPF(1, "midiOutUnprepareHeader failed in seqBufferDone! (%lu)", (DWORD)mmrc);
  810.         }
  811.  
  812.         if (0 == pSeq->uBuffersInMMSYSTEM)
  813.         {
  814.             DPF(1, "seqBufferDone: normal sequencer shutdown.");
  815.             
  816.             /* Totally done! Free device and notify.
  817.             */
  818.             midiStreamClose(pSeq->hmidi);
  819.             
  820.             pSeq->hmidi = NULL;
  821.             pSeq->uState = SEQ_S_OPENED;
  822.             pSeq->mmrcLastErr = MMSYSERR_NOERROR;
  823.             pSeq->fdwSeq &= ~SEQ_F_WAITING;
  824.         
  825.             // lParam indicates whether or not to preroll again. Don't if we were explicitly
  826.             // stopped.
  827.             //    
  828.             PostMessage(pSeq->hWnd, MMSG_DONE, (WPARAM)pSeq, (LPARAM)(SEQ_S_STOPPING != pSeq->uState));
  829.         }
  830.     }
  831.     else
  832.     {
  833.         /*
  834.         ** Not EOF yet; attempt to fill another buffer
  835.         */
  836.         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
  837.         
  838.         switch(smfrc)
  839.         {
  840.             case SMF_SUCCESS:
  841.                 break;
  842.  
  843.             case SMF_END_OF_FILE:
  844.                 pSeq->fdwSeq |= SEQ_F_EOF;
  845.                 smfrc = SMF_SUCCESS;
  846.                 break;
  847.  
  848.             default:
  849.                 DPF(1, "smfReadEvents returned %lu in callback!", (DWORD)smfrc);
  850.                 pSeq->uState = SEQ_S_STOPPING;
  851.                 break;
  852.         }
  853.  
  854.         if (SMF_SUCCESS == smfrc)
  855.         {
  856.             ++pSeq->uBuffersInMMSYSTEM;
  857.             mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh));
  858.             if (MMSYSERR_NOERROR != mmrc)
  859.             {
  860.                 DPF(1, "seqBufferDone(): midiStreamOut() returned %lu!", (DWORD)mmrc);
  861.                 
  862.                 --pSeq->uBuffersInMMSYSTEM;
  863.                 pSeq->uState = SEQ_S_STOPPING;
  864.             }
  865.         }
  866.     }
  867. }
  868.  
  869. /***************************************************************************
  870. *  
  871. * XlatSMFErr
  872. *
  873. * Translates an error from the SMF layer into an appropriate MCI error.
  874. *
  875. * smfrc                     - The return code from any SMF function.
  876. *
  877. * Returns
  878. *   A parallel error from the MCI error codes.   
  879. *
  880. ***************************************************************************/
  881. PRIVATE MMRESULT FNLOCAL XlatSMFErr(
  882.     SMFRESULT               smfrc)
  883. {
  884.     switch(smfrc)
  885.     {
  886.         case SMF_SUCCESS:
  887.             return MMSYSERR_NOERROR;
  888.  
  889.         case SMF_NO_MEMORY:
  890.             return MCIERR_OUT_OF_MEMORY;
  891.  
  892.         case SMF_INVALID_FILE:
  893.         case SMF_OPEN_FAILED:
  894.         case SMF_INVALID_TRACK:
  895.             return MCIERR_INVALID_FILE;
  896.  
  897.         default:
  898.             return MCIERR_UNSUPPORTED_FUNCTION;
  899.     }
  900. }
  901.  
  902.  
  903.  
  904.  
  905.