home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / TOOLS / MIDIPLYR / SEQUENCE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  29.7 KB  |  1,013 lines

  1. /*****************************************************************************
  2. *
  3. *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  4. *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  5. *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
  6. *  A PARTICULAR PURPOSE.
  7. *
  8. *  Copyright (C) 1993, 1994 Microsoft Corporation. All Rights Reserved.
  9. *
  10. ******************************************************************************
  11. *
  12. * 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 FNLOCAL seqFinishPreroll(PSEQ pSeq, LPMIDIHDR lpmh);
  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.     DPF(1, "seqOpenFile: tkLength %lu", (DWORD)pSeq->tkLength);
  200.  
  201. Seq_Open_File_Cleanup:    
  202.     if (MMSYSERR_NOERROR != rc)
  203.         seqCloseFile(pSeq);
  204.     else
  205.         pSeq->uState = SEQ_S_OPENED;
  206.  
  207.     return rc;
  208. }
  209.  
  210. /***************************************************************************
  211. *  
  212. * seqCloseFile
  213. *
  214. * Deassociates a MIDI file with the given sequencer instance.
  215. *
  216. * pSeq                      -  The sequencer instance.
  217. *
  218. * Returns
  219. *   MMSYSERR_NOERROR If the operation is successful.
  220. *    
  221. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  222. *     stopped.
  223. *     
  224. * A call to seqCloseFile must be paired with a prior call to
  225. * seqOpenFile. All buffers associated with the file will be
  226. * freed and the file will be closed. The sequencer must be
  227. * stopped before this call will be accepted.
  228. *
  229. ***************************************************************************/
  230. MMRESULT FNLOCAL seqCloseFile(
  231.     PSEQ                    pSeq)
  232. {
  233.     LPMIDIHDR               lpmh;
  234.     
  235.     assert(pSeq != NULL);
  236.     
  237.     if (SEQ_S_PREROLLED != pSeq->uState && SEQ_S_OPENED != pSeq->uState)
  238.         return MCIERR_UNSUPPORTED_FUNCTION;
  239.     
  240.     if ((HSMF)NULL != pSeq->hSmf)
  241.     {
  242.         smfCloseFile(pSeq->hSmf);
  243.         pSeq->hSmf = (HSMF)NULL;
  244.     }
  245.  
  246.     /* If we were prerolled, need to clean up -- have an open MIDI handle
  247.     ** and buffers in the ready queue
  248.     */
  249.  
  250.     while (pSeq->lpmhReadyFront)
  251.     {
  252.         lpmh = pSeq->lpmhReadyFront;
  253.         pSeq->lpmhReadyFront = lpmh->lpNext;
  254.  
  255.         if (pSeq->hmidi != NULL)
  256.             midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh));
  257.  
  258.         lpmh->lpNext = pSeq->lpmhFree;
  259.         pSeq->lpmhFree = lpmh;
  260.     }
  261.  
  262.     pSeq->lpmhReadyRear = NULL;
  263.  
  264.     if (pSeq->hmidi != NULL)
  265.     {
  266.         midiStreamClose(pSeq->hmidi);
  267.         pSeq->hmidi = NULL;
  268.     }
  269.  
  270.     pSeq->uState = SEQ_S_NOFILE;
  271.  
  272.     return MMSYSERR_NOERROR;
  273. }
  274.  
  275. /***************************************************************************
  276. *  
  277. * seqPreroll
  278. *
  279. * Prepares the file for playback at the given position.
  280. *
  281. * pSeq                      - The sequencer instance.
  282. *
  283. * lpPreroll                 - Specifies the starting and ending tick
  284. *                             positions to play between.
  285. *
  286. * Returns
  287. *   MMSYSERR_NOERROR If the operation is successful.
  288. *    
  289. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  290. *     opened or prerolled.
  291. *
  292. * Open the device so we can initialize channels.
  293. *
  294. * Loop through the tracks. For each track, seek to the given position and
  295. * send the init data SMF gives us to the handle.
  296. *
  297. * Wait for all init buffers to finish.
  298. *
  299. * Unprepare the buffers (they're only ever sent here; the sequencer
  300. * engine merges them into a single stream during normal playback) and
  301. * refill them with the first chunk of data from the track. 
  302. *
  303. *     
  304. ****************************************************************************/
  305. MMRESULT FNLOCAL seqPreroll(
  306.     PSEQ                    pSeq,
  307.     LPPREROLL               lpPreroll)
  308. {
  309.     SMFRESULT               smfrc;
  310.     MMRESULT                mmrc        = MMSYSERR_NOERROR;
  311.     MIDIPROPTIMEDIV         mptd;
  312.     LPMIDIHDR               lpmh        = NULL;
  313.     UINT                    uDeviceID;
  314.  
  315.     DPF(1, "seqPreroll Part 1");
  316.     
  317.     assert(pSeq != NULL);
  318.  
  319.     if (pSeq->uState != SEQ_S_OPENED &&
  320.         pSeq->uState != SEQ_S_PREROLLED)
  321.         return MCIERR_UNSUPPORTED_FUNCTION;
  322.  
  323.     pSeq->fdwSeq |= SEQ_F_WAITING;
  324.  
  325.     pSeq->tkBase = lpPreroll->tkBase;
  326.     pSeq->tkEnd  = lpPreroll->tkEnd;
  327.     pSeq->uBuffersInMMSYSTEM = 0;
  328.  
  329.     if (pSeq->uState == SEQ_S_PREROLLED)
  330.     {
  331.         lpmh = pSeq->lpmhReadyFront;
  332.         while (lpmh != NULL)
  333.         {
  334.             pSeq->lpmhReadyFront = pSeq->lpmhReadyFront->lpNext;
  335.  
  336.             lpmh->lpNext = pSeq->lpmhFree;
  337.             pSeq->lpmhFree = lpmh;
  338.             
  339.             lpmh = pSeq->lpmhReadyFront;
  340.         }
  341.  
  342.         pSeq->lpmhReadyRear = NULL;
  343.     }
  344.     
  345.     pSeq->uState = SEQ_S_PREROLLING;
  346.     
  347.     /*
  348.     ** We've successfully opened the file; now
  349.     ** open the MIDI device and set the time division.
  350.     **
  351.     ** NOTE: seqPreroll is equivalent to seek; device might already be open
  352.     */
  353.     if (NULL == pSeq->hmidi)
  354.     {
  355.         DPF(1, "seqPreroll: midiStreamOpen");
  356.         uDeviceID = pSeq->uDeviceID;
  357.         if ((mmrc = midiStreamOpen(&pSeq->hmidi,
  358.                                    &uDeviceID,
  359.                                    1,
  360.                                    (DWORD)(WORD)pSeq->hWnd,
  361.                                    0,
  362.                                    CALLBACK_WINDOW)) != MMSYSERR_NOERROR)
  363.         {
  364.             DPF(1, "midiStreamOpen() -> %lu", (DWORD)mmrc);
  365.             mmrc = MCIERR_DEVICE_NOT_READY;
  366.             goto seq_Preroll_Cleanup;
  367.         }
  368.         
  369.         DPF(1, "seqPreroll: midiStreamOpen done HMIDI=%04X", (WORD)pSeq->hmidi);
  370.         
  371.         mptd.cbStruct  = sizeof(mptd);
  372.         mptd.dwTimeDiv = pSeq->dwTimeDivision;
  373.         if ((mmrc = midiStreamProperty(
  374.                                        (HMIDI)pSeq->hmidi,
  375.                                        (LPBYTE)&mptd,
  376.                                        MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR)
  377.         {
  378.             DPF(1, "midiStreamProperty() -> %04X", (WORD)mmrc);
  379.             midiStreamClose(pSeq->hmidi);
  380.             mmrc = MCIERR_DEVICE_NOT_READY;
  381.             goto seq_Preroll_Cleanup;
  382.         }
  383.  
  384.         DPF(1, "seqPreroll: midiStreamProperty done!");
  385.     }
  386.  
  387.     assert((HMIDIOUT)NULL != pSeq->hmidi);
  388.     assert(NULL != pSeq->lpmhFree);
  389.  
  390.     lpmh = pSeq->lpmhFree;
  391.     pSeq->lpmhFree = lpmh->lpNext;
  392.  
  393.     if (SMF_SUCCESS != (smfrc = smfSeek(pSeq->hSmf, pSeq->tkBase, lpmh)))
  394.     {
  395.         DPF(1, "smfSeek() returned %lu", (DWORD)smfrc);
  396.         mmrc = XlatSMFErr(smfrc);
  397.         goto seq_Preroll_Cleanup;
  398.     }
  399.  
  400.     if (lpmh->dwBytesRecorded)
  401.     {
  402.         DPF(1, "sizeof(*lpmh) %u", sizeof(*lpmh));
  403.         if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  404.         {
  405.             DPF(1, "midiOutPrepare(preroll) -> %lu!", (DWORD)mmrc);
  406.  
  407.             mmrc = MCIERR_DEVICE_NOT_READY;
  408.             goto seq_Preroll_Cleanup;
  409.         }
  410.  
  411.         ++pSeq->uBuffersInMMSYSTEM;
  412.  
  413.         if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  414.         {
  415.             DPF(1, "midiStreamOut(preroll) -> %lu!", (DWORD)mmrc);
  416.  
  417.             mmrc = MCIERR_DEVICE_NOT_READY;
  418.             --pSeq->uBuffersInMMSYSTEM;
  419.             goto seq_Preroll_Cleanup;
  420.         }
  421.     }
  422.     else
  423.     {
  424.         // Empty buffer, we'll never get to preroll cleanup
  425.         //
  426.         seqFinishPreroll(pSeq, lpmh);
  427.     }
  428.  
  429. seq_Preroll_Cleanup:
  430.     if (MMSYSERR_NOERROR != mmrc)
  431.     {
  432.         DPF(1, "seqPreroll Part 1 failure");
  433.         if (NULL != lpmh)
  434.         {
  435.             if (lpmh->dwFlags & MHDR_PREPARED)
  436.             {
  437.                 midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(lpmh));
  438.             }
  439.  
  440.             lpmh->lpNext = pSeq->lpmhFree;
  441.             pSeq->lpmhFree = lpmh;
  442.         }
  443.  
  444.         /* Only on success will seqFinishPreroll be called from the
  445.         ** callback to complete -- in any other case, indicate immediate
  446.         ** failure so background task doesn't block waiting for us
  447.         */
  448.         pSeq->uState = SEQ_S_OPENED;
  449.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  450.     }
  451.     else
  452.     {
  453.         DPF(1, "seqPreroll Part 1 success");
  454.     }
  455.  
  456.     return mmrc;
  457. }
  458.  
  459. /***************************************************************************
  460. *  
  461. * seqFinishPreroll
  462. *
  463. * Completes the preroll operation after the seek buffer has been sent.
  464. *
  465. * pSeq                      - The sequencer instance.
  466. *
  467. * lpPreroll                 - Specifies the starting and ending tick
  468. *                             positions to play between.
  469. *
  470. * Put the seek buffer back into the free list.
  471. *
  472. * Read as much of the file as we can into the buffers from the free list.
  473. * Prepare them and put them into the ready queue.
  474. * Send an MMSG_DONE to the callback to let them know we're ready to play.
  475. *     
  476. ****************************************************************************/
  477. PRIVATE VOID FNLOCAL seqFinishPreroll(
  478.     PSEQ                    pSeq,
  479.     LPMIDIHDR               lpmh)
  480. {
  481.     SMFRESULT               smfrc;
  482.     MMRESULT                mmrc;
  483.  
  484.     /* lpmh is the first header from the free queue -- use it and the
  485.     ** rest to gather the first 'n' buffers of MIDI data and prepare
  486.     ** the headers.
  487.     ** 
  488.     ** NOTE: Preparing the already prepared buffer is benign;
  489.     ** midiOutPrepareHeader() will just succeed if the prepare flag
  490.     ** is already set.
  491.     */
  492.     assert(NULL == pSeq->lpmhReadyRear);
  493.  
  494.     DPF(1, "seqPreroll Part 2");
  495.  
  496.     pSeq->fdwSeq &= ~SEQ_F_EOF;
  497.  
  498.     do
  499.     {
  500.         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
  501.  
  502.         if (SMF_SUCCESS != smfrc && SMF_END_OF_FILE != smfrc)
  503.         {
  504.             DPF(1, "SFP: smfReadEvents() -> %u", (UINT)smfrc);
  505.             pSeq->mmrcLastErr = XlatSMFErr(smfrc);
  506.             goto seq_Finish_Preroll_Cleanup;
  507.         }
  508.  
  509.         if (MMSYSERR_NOERROR != midiOutPrepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh)))
  510.         {
  511.             DPF(1, "SFP: midiOutPrepareHeader failed");
  512.             pSeq->mmrcLastErr = MCIERR_DEVICE_NOT_READY;
  513.             goto seq_Finish_Preroll_Cleanup;
  514.         }
  515.  
  516.         if (NULL == pSeq->lpmhReadyRear)
  517.         {
  518.             pSeq->lpmhReadyRear = pSeq->lpmhReadyFront = lpmh;
  519.         }
  520.         else
  521.         {
  522.             pSeq->lpmhReadyRear->lpNext = lpmh;
  523.             pSeq->lpmhReadyRear         = lpmh;
  524.         }
  525.  
  526.         lpmh->lpNext = NULL;
  527.  
  528.         if (SMF_END_OF_FILE == smfrc)
  529.         {
  530.             pSeq->fdwSeq |= SEQ_F_EOF;
  531.             break;
  532.         }
  533.  
  534.         if (NULL != (lpmh = pSeq->lpmhFree))
  535.         {
  536.             pSeq->lpmhFree = pSeq->lpmhFree->lpNext;
  537.         }
  538.     } while (NULL != lpmh);
  539.  
  540. seq_Finish_Preroll_Cleanup:
  541.  
  542.     if (MMSYSERR_NOERROR != pSeq->mmrcLastErr)
  543.     {
  544.         DPF(1, "seqPreroll Part 2 failure [%lu]", (DWORD)pSeq->mmrcLastErr);
  545.         lpmh = pSeq->lpmhReadyFront;
  546.  
  547.         while (lpmh != NULL)
  548.         {
  549.             pSeq->lpmhReadyFront = lpmh->lpNext;
  550.  
  551.             mmrc = midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh));
  552.             if (MMSYSERR_NOERROR != mmrc)
  553.             {
  554.                 DPF(1, "midiOutUnprepareHeader failed in cleanup!! (%lu)", (DWORD)mmrc);
  555.             }
  556.  
  557.             lpmh->lpNext = pSeq->lpmhFree;
  558.             pSeq->lpmhFree = lpmh;
  559.  
  560.             lpmh = pSeq->lpmhReadyFront;
  561.         }
  562.  
  563.         pSeq->lpmhReadyRear = NULL;
  564.  
  565.         pSeq->uState = SEQ_S_OPENED;
  566.     }
  567.     else
  568.     {
  569.         pSeq->uState = SEQ_S_PREROLLED;
  570.         DPF(1, "seqPreroll Part 2 success");
  571.     }
  572.  
  573.     pSeq->fdwSeq &= ~SEQ_F_WAITING;
  574.  
  575.     PostMessage(pSeq->hWnd, MMSG_DONE, (WPARAM)pSeq, 0L);
  576. }
  577.  
  578. /***************************************************************************
  579. *  
  580. * seqStart
  581. *
  582. * Starts playback at the current position.
  583. *
  584. * pSeq                      - The sequencer instance.
  585. *
  586. * Returns
  587. *   MMSYSERR_NOERROR If the operation is successful.
  588. *    
  589. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  590. *     stopped.
  591. *
  592. *   MCIERR_DEVICE_NOT_READY If the underlying MIDI device could
  593. *     not be opened or fails any call.
  594. * The sequencer must be prerolled before seqStart may be called.
  595. *
  596. * Just feed everything in the ready queue to the device.
  597. *       
  598. ***************************************************************************/
  599. MMRESULT FNLOCAL seqStart(
  600.     PSEQ                    pSeq)
  601. {
  602.     MMRESULT                mmrc;
  603.     LPMIDIHDR               lpmWork;
  604.  
  605.     assert(NULL != pSeq);
  606.  
  607.     if (SEQ_S_PREROLLED != pSeq->uState)
  608.     {
  609.         DPF(1, "seqStart(): State is wrong! [%u]", pSeq->uState);
  610.         return MCIERR_UNSUPPORTED_FUNCTION;
  611.     }
  612.  
  613.     pSeq->uBuffersInMMSYSTEM = 0;
  614.     pSeq->uState = SEQ_S_PLAYING;
  615.     pSeq->fdwSeq |= SEQ_F_WAITING;
  616.  
  617.     DPF(1, "About to feed ready queue to MMSYSTEM [First %08lX]", (DWORD)(pSeq->lpmhReadyFront));
  618.  
  619.     while (pSeq->lpmhReadyFront)
  620.     {
  621.         /* NOTE: Don't need critical section here because no-one will
  622.         ** be reinserting into this queue until after we're stopped again
  623.         */
  624.         lpmWork = pSeq->lpmhReadyFront;
  625.         if (NULL == (pSeq->lpmhReadyFront = lpmWork->lpNext))
  626.             pSeq->lpmhReadyRear = NULL;
  627.  
  628.         ++pSeq->uBuffersInMMSYSTEM;
  629.         #ifdef DEBUG    // this block can be removed when I get midiStreamOut to work
  630.         if( IsBadWritePtr( lpmWork->lpData, lpmWork->dwBufferLength ) )
  631.             MessageBox( NULL, "bad MIDIHDR.lpdata", "debug info", MB_OK );
  632.         if( lpmWork->dwBytesRecorded > lpmWork->dwBufferLength )
  633.             MessageBox( NULL, "lpmWork->dwBytesRecorded > lpmWork->dwBufferLength", "debug info", MB_OK );
  634.         #endif
  635.         if ((mmrc = midiStreamOut(pSeq->hmidi, lpmWork, sizeof(*lpmWork))) != MMSYSERR_NOERROR)
  636.         {
  637.             DPF(1, "midiStreamOut failed!");
  638.             
  639.             --pSeq->uBuffersInMMSYSTEM;
  640.             pSeq->uState = SEQ_S_STOPPING;
  641.             midiOutReset(pSeq->hmidi);
  642.             
  643.             return MCIERR_DEVICE_NOT_READY;
  644.         }
  645.     }
  646.  
  647.     return MMSYSERR_NOERROR;
  648. }
  649.  
  650. /***************************************************************************
  651. *  
  652. * seqPause
  653. *
  654. * Pauses playback of the instance.
  655. *
  656. * pSeq                      - The sequencer instance.
  657. *
  658. * Returns
  659. *   MMSYSERR_NOERROR If the operation is successful.
  660. *    
  661. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  662. *     playing.
  663. *
  664. * The sequencer must be playing before seqPause may be called.
  665. * Pausing the sequencer will cause all currently on notes to be turned
  666. * off. This may cause playback to be slightly inaccurate on restart
  667. * due to missing notes.
  668. *       
  669. ***************************************************************************/
  670. MMRESULT FNLOCAL seqPause(
  671.     PSEQ                    pSeq)
  672. {
  673.     assert(NULL != pSeq);
  674.     
  675.     if (SEQ_S_PLAYING != pSeq->uState)
  676.         return MCIERR_UNSUPPORTED_FUNCTION;
  677.  
  678.     pSeq->uState = SEQ_S_PAUSED;
  679.     midiStreamPause(pSeq->hmidi);
  680.     
  681.     return MMSYSERR_NOERROR;
  682. }
  683.  
  684. /***************************************************************************
  685. *  
  686. * seqRestart
  687. *
  688. * Restarts playback of an instance after a pause.
  689. *
  690. * pSeq                      - The sequencer instance.
  691. *
  692. * Returns
  693. *    MMSYSERR_NOERROR If the operation is successful.
  694. *    
  695. *    MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  696. *     paused.
  697. *
  698. * The sequencer must be paused before seqRestart may be called.
  699. *
  700. ***************************************************************************/
  701. MMRESULT FNLOCAL seqRestart(
  702.     PSEQ                    pSeq)
  703. {
  704.     assert(NULL != pSeq);
  705.     
  706.     if (SEQ_S_PAUSED != pSeq->uState)
  707.         return MCIERR_UNSUPPORTED_FUNCTION;
  708.  
  709.     pSeq->uState = SEQ_S_PLAYING;
  710.     midiStreamRestart(pSeq->hmidi);
  711.  
  712.     return MMSYSERR_NOERROR;
  713. }
  714.  
  715. /***************************************************************************
  716. *  
  717. * seqStop
  718. *
  719. * Totally stops playback of an instance.
  720. *
  721. * pSeq                      - The sequencer instance.
  722. *
  723. * Returns
  724. *   MMSYSERR_NOERROR If the operation is successful.
  725. *    
  726. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  727. *     paused or playing.
  728. *
  729. * The sequencer must be paused or playing before seqStop may be called.
  730. *
  731. ***************************************************************************/
  732. MMRESULT FNLOCAL seqStop(
  733.     PSEQ                    pSeq)
  734. {
  735.     assert(NULL != pSeq);
  736.  
  737.     /* Automatic success if we're already stopped
  738.     */
  739.     if (SEQ_S_PLAYING != pSeq->uState &&
  740.         SEQ_S_PAUSED != pSeq->uState)
  741.     {
  742.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  743.         return MMSYSERR_NOERROR;
  744.     }
  745.  
  746.     pSeq->uState = SEQ_S_STOPPING;
  747.     pSeq->fdwSeq |= SEQ_F_WAITING;
  748.     
  749.     if (MMSYSERR_NOERROR != (pSeq->mmrcLastErr = midiStreamStop(pSeq->hmidi)))
  750.     {
  751.         DPF(1, "midiOutStop() returned %lu in seqStop()!", (DWORD)pSeq->mmrcLastErr);
  752.         
  753.         pSeq->fdwSeq &= ~SEQ_F_WAITING;
  754.         return MCIERR_DEVICE_NOT_READY;
  755.     }
  756.     
  757.     return MMSYSERR_NOERROR;
  758. }
  759.  
  760. /***************************************************************************
  761. *  
  762. * seqTime
  763. *
  764. * Determine the current position in playback of an instance.
  765. *
  766. * pSeq                      - The sequencer instance.
  767. *
  768. * pTicks                    - A pointer to a DWORD where the current position
  769. *                             in ticks will be returned.
  770. *
  771. * Returns
  772. *   MMSYSERR_NOERROR If the operation is successful.
  773. *
  774. *   MCIERR_DEVICE_NOT_READY If the underlying device fails to report
  775. *     the position.
  776. *    
  777. *   MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
  778. *     paused or playing.
  779. *
  780. * The sequencer must be paused, playing or prerolled before seqTime
  781. * may be called.
  782. *
  783. ***************************************************************************/
  784. MMRESULT FNLOCAL seqTime(
  785.     PSEQ                    pSeq,
  786.     PTICKS                  pTicks)
  787. {
  788.     MMRESULT                mmr;
  789.     MMTIME                  mmt;
  790.     
  791.     assert(pSeq != NULL);
  792.  
  793.     if (SEQ_S_PLAYING != pSeq->uState &&
  794.         SEQ_S_PAUSED != pSeq->uState &&
  795.         SEQ_S_PREROLLING != pSeq->uState &&
  796.         SEQ_S_PREROLLED != pSeq->uState &&
  797.         SEQ_S_OPENED != pSeq->uState)
  798.     {
  799.         DPF(1, "seqTime(): State wrong! [is %u]", pSeq->uState);
  800.         return MCIERR_UNSUPPORTED_FUNCTION;
  801.     }
  802.  
  803.     *pTicks = 0;
  804.     if (SEQ_S_OPENED != pSeq->uState)
  805.     {
  806.         *pTicks = pSeq->tkBase;
  807.         if (SEQ_S_PREROLLED != pSeq->uState)
  808.         {
  809.             mmt.wType = TIME_TICKS;
  810.             mmr = midiStreamPosition(pSeq->hmidi, &mmt, sizeof(mmt));
  811.             if (MMSYSERR_NOERROR != mmr)
  812.             {
  813.                 DPF(1, "midiStreamPosition() returned %lu", (DWORD)mmr);
  814.                 return MCIERR_DEVICE_NOT_READY;
  815.             }
  816.  
  817.             *pTicks += mmt.u.ticks;
  818.         }
  819.     }
  820.  
  821.     return MMSYSERR_NOERROR;
  822. }
  823.                               
  824. /***************************************************************************
  825. *  
  826. * seqMillisecsToTicks
  827. *
  828. * Given a millisecond offset in the output stream, returns the associated
  829. * tick position.
  830. *
  831. * pSeq                      - The sequencer instance.
  832. *
  833. * msOffset                  - The millisecond offset into the stream.
  834. *
  835. * Returns the number of ticks into the stream.
  836. *
  837. ***************************************************************************/
  838. TICKS FNLOCAL seqMillisecsToTicks(
  839.     PSEQ                    pSeq,
  840.     DWORD                   msOffset)
  841. {
  842.     return smfMillisecsToTicks(pSeq->hSmf, msOffset);
  843. }
  844.  
  845. /***************************************************************************
  846. *  
  847. * seqTicksToMillisecs
  848. *
  849. * Given a tick offset in the output stream, returns the associated
  850. * millisecond position.
  851. *
  852. * pSeq                      - The sequencer instance.
  853. *
  854. * tkOffset                  - The tick offset into the stream.
  855. *
  856. * Returns the number of milliseconds into the stream.
  857. *
  858. ***************************************************************************/
  859. DWORD FNLOCAL seqTicksToMillisecs(
  860.     PSEQ                    pSeq,
  861.     TICKS                   tkOffset)
  862. {
  863.     return smfTicksToMillisecs(pSeq->hSmf, tkOffset);
  864. }
  865.  
  866. /***************************************************************************
  867. *  
  868. * seqBufferDone
  869. *
  870. * Specifies that the given buffer has completed playback.
  871. *
  872. * lpmh                      - The buffer that has completed playback.
  873. *
  874. * This function should be called by the user whenever he receives an
  875. * MM_MOM_DONE message. 
  876. *
  877. ***************************************************************************/
  878. VOID FNLOCAL seqBufferDone(
  879.     LPMIDIHDR               lpmh)
  880. {
  881.     PSEQ                    pSeq;
  882.     MMRESULT                mmrc;
  883.     SMFRESULT               smfrc;
  884.  
  885.     assert(lpmh != NULL);
  886.     
  887.     pSeq = (PSEQ)(lpmh->dwUser);
  888.  
  889.     assert(pSeq != NULL);
  890.  
  891.     --pSeq->uBuffersInMMSYSTEM;
  892.     
  893.     /* If we're SEQ_S_PREROLLING, we're doing a preroll -- don't try to refill
  894.     ** the buffer
  895.     */
  896.     if (SEQ_S_PREROLLING == pSeq->uState)
  897.     {
  898.         /* If all buffers have completed, we've completed the preroll --
  899.         ** call the completion routine to clean up and notify foreground.
  900.         **
  901.         ** This should only ever be one buffer
  902.         */
  903.         if (0 == pSeq->uBuffersInMMSYSTEM)
  904.             seqFinishPreroll(pSeq, lpmh);
  905.         
  906.         return;
  907.     }
  908.  
  909.     if ((SEQ_S_STOPPING == pSeq->uState) || (pSeq->fdwSeq & SEQ_F_EOF))
  910.     {
  911.         DPF(1, "EOF set or stopping");
  912.         /*
  913.         ** Reached EOF, just put the buffer back on the free
  914.         ** list 
  915.         */
  916.         lpmh->lpNext   = pSeq->lpmhFree;
  917.         pSeq->lpmhFree = lpmh;
  918.  
  919.         if (MMSYSERR_NOERROR != (mmrc = midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
  920.         {
  921.             DPF(1, "midiOutUnprepareHeader failed in seqBufferDone! (%lu)", (DWORD)mmrc);
  922.         }
  923.  
  924.         if (0 == pSeq->uBuffersInMMSYSTEM)
  925.         {
  926.             DPF(1, "seqBufferDone: normal sequencer shutdown.");
  927.             
  928.             /* Totally done! Free device and notify.
  929.             */
  930.             midiOutReset(pSeq->hmidi);
  931.             midiStreamClose(pSeq->hmidi);
  932.             
  933.             pSeq->hmidi = NULL;
  934.             pSeq->uState = SEQ_S_OPENED;
  935.             pSeq->mmrcLastErr = MMSYSERR_NOERROR;
  936.             pSeq->fdwSeq &= ~SEQ_F_WAITING;
  937.             
  938.             PostMessage(pSeq->hWnd, MMSG_DONE, (WPARAM)pSeq, 0L);
  939.         }
  940.     }
  941.     else
  942.     {
  943.         /*
  944.         ** Not EOF yet; attempt to fill another buffer
  945.         */
  946.         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
  947.         
  948.         switch(smfrc)
  949.         {
  950.             case SMF_SUCCESS:
  951.                 break;
  952.  
  953.             case SMF_END_OF_FILE:
  954.                 pSeq->fdwSeq |= SEQ_F_EOF;
  955.                 smfrc = SMF_SUCCESS;
  956.                 break;
  957.  
  958.             default:
  959.                 DPF(1, "smfReadEvents returned %lu in callback!", (DWORD)smfrc);
  960.                 pSeq->uState = SEQ_S_STOPPING;
  961.                 break;
  962.         }
  963.  
  964.         if (SMF_SUCCESS == smfrc)
  965.         {
  966.             ++pSeq->uBuffersInMMSYSTEM;
  967.             mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh));
  968.             if (MMSYSERR_NOERROR != mmrc)
  969.             {
  970.                 DPF(1, "seqBufferDone(): midiStreamOut() returned %lu!", (DWORD)mmrc);
  971.                 
  972.                 --pSeq->uBuffersInMMSYSTEM;
  973.                 pSeq->uState = SEQ_S_STOPPING;
  974.             }
  975.         }
  976.     }
  977. }
  978.  
  979. /***************************************************************************
  980. *  
  981. * XlatSMFErr
  982. *
  983. * Translates an error from the SMF layer into an appropriate MCI error.
  984. *
  985. * smfrc                     - The return code from any SMF function.
  986. *
  987. * Returns
  988. *   A parallel error from the MCI error codes.   
  989. *
  990. ***************************************************************************/
  991. PRIVATE MMRESULT FNLOCAL XlatSMFErr(
  992.     SMFRESULT               smfrc)
  993. {
  994.     switch(smfrc)
  995.     {
  996.         case SMF_SUCCESS:
  997.             return MMSYSERR_NOERROR;
  998.  
  999.         case SMF_NO_MEMORY:
  1000.             return MCIERR_OUT_OF_MEMORY;
  1001.  
  1002.         case SMF_INVALID_FILE:
  1003.         case SMF_OPEN_FAILED:
  1004.         case SMF_INVALID_TRACK:
  1005.             return MCIERR_INVALID_FILE;
  1006.  
  1007.         default:
  1008.             return MCIERR_UNSUPPORTED_FUNCTION;
  1009.     }
  1010. }
  1011.  
  1012.