home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- *
- * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
- * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
- * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
- * A PARTICULAR PURPOSE.
- *
- * Copyright (C) 1993 - 1997 Microsoft Corporation. All Rights Reserved.
- *
- ******************************************************************************
- *
- * Sequence.C
- *
- * Sequencer engine for MIDI player app
- *
- *****************************************************************************/
-
- #include <windows.h>
- #include <windowsx.h>
- #include <mmsystem.h>
- #include <limits.h>
-
- #include "debug.h"
- #include "seq.h"
-
- PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
- PRIVATE MMRESULT FNLOCAL XlatSMFErr(SMFRESULT smfrc);
-
- /***************************************************************************
- *
- * seqAllocBuffers
- *
- * Allocate buffers for this instance.
- *
- * pSeq - The sequencer instance to allocate buffers for.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation was successful.
- *
- * MCIERR_OUT_OF_MEMORY If there is insufficient memory for
- * the requested number and size of buffers.
- *
- * seqAllocBuffers allocates playback buffers based on the
- * cbBuffer and cBuffer fields of pSeq. cbBuffer specifies the
- * number of bytes in each buffer, and cBuffer specifies the
- * number of buffers to allocate.
- *
- * seqAllocBuffers must be called before any other sequencer call
- * on a newly allocted SEQUENCE structure. It must be paired with
- * a call to seqFreeBuffers, which should be the last call made
- * before the SEQUENCE structure is discarded.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqAllocBuffers(
- PSEQ pSeq)
- {
- DWORD dwEachBufferSize;
- DWORD dwAlloc;
- UINT i;
- LPBYTE lpbWork;
-
- assert(pSeq != NULL);
-
- pSeq->uState = SEQ_S_NOFILE;
- pSeq->lpmhFree = NULL;
- pSeq->lpbAlloc = NULL;
- pSeq->hSmf = (HSMF)NULL;
-
- /* First make sure we can allocate the buffers they asked for
- */
- dwEachBufferSize = sizeof(MIDIHDR) + (DWORD)(pSeq->cbBuffer);
- dwAlloc = dwEachBufferSize * (DWORD)(pSeq->cBuffer);
-
- pSeq->lpbAlloc = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, dwAlloc);
- if (NULL == pSeq->lpbAlloc)
- return MCIERR_OUT_OF_MEMORY;
-
- /* Initialize all MIDIHDR's and throw them into a free list
- */
- pSeq->lpmhFree = NULL;
-
- lpbWork = pSeq->lpbAlloc;
- for (i=0; i < pSeq->cBuffer; i++)
- {
- ((LPMIDIHDR)lpbWork)->lpNext = pSeq->lpmhFree;
-
- ((LPMIDIHDR)lpbWork)->lpData = lpbWork + sizeof(MIDIHDR);
- ((LPMIDIHDR)lpbWork)->dwBufferLength = pSeq->cbBuffer;
- ((LPMIDIHDR)lpbWork)->dwBytesRecorded = 0;
- ((LPMIDIHDR)lpbWork)->dwUser = (DWORD)(UINT)pSeq;
- ((LPMIDIHDR)lpbWork)->dwFlags = 0;
-
- pSeq->lpmhFree = (LPMIDIHDR)lpbWork;
-
- lpbWork += dwEachBufferSize;
- }
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqFreeBuffers
- *
- * Free buffers for this instance.
- *
- * pSeq - The sequencer instance to free buffers for.
- *
- * seqFreeBuffers frees all allocated memory belonging to the
- * given sequencer instance pSeq. It must be the last call
- * performed on the instance before it is destroyed.
- *
- ****************************************************************************/
- VOID FNLOCAL seqFreeBuffers(
- PSEQ pSeq)
- {
- LPMIDIHDR lpmh;
-
- assert(pSeq != NULL);
-
- if (NULL != pSeq->lpbAlloc)
- {
- lpmh = (LPMIDIHDR)pSeq->lpbAlloc;
- assert(!(lpmh->dwFlags & MHDR_PREPARED));
-
- GlobalFreePtr(pSeq->lpbAlloc);
- }
- }
-
- /***************************************************************************
- *
- * seqOpenFile
- *
- * Associates a MIDI file with the given sequencer instance.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If there is already a file open
- * on this instance.
- *
- * MCIERR_OUT_OF_MEMORY If there was insufficient memory to
- * allocate internal buffers on the file.
- *
- * MCIERR_INVALID_FILE If initial attempts to parse the file
- * failed (such as the file is not a MIDI or RMI file).
- *
- * seqOpenFile may only be called if there is no currently open file
- * on the instance. It must be paired with a call to seqCloseFile
- * when operations on this file are complete.
- *
- * The pstrFile field of pSeq contains the name of the file
- * to open. This name will be passed directly to mmioOpen; it may
- * contain a specifcation for a custom MMIO file handler. The task
- * context used for all I/O will be the task which calls seqOpenFile.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqOpenFile(
- PSEQ pSeq)
- {
- MMRESULT rc = MMSYSERR_NOERROR;
- SMFOPENFILESTRUCT sofs;
- SMFFILEINFO sfi;
- SMFRESULT smfrc;
- DWORD cbBuffer;
-
- assert(pSeq != NULL);
-
- if (pSeq->uState != SEQ_S_NOFILE)
- {
- return MCIERR_UNSUPPORTED_FUNCTION;
- }
-
- assert(pSeq->pstrFile != NULL);
-
- sofs.pstrName = pSeq->pstrFile;
-
- smfrc = smfOpenFile(&sofs);
- if (SMF_SUCCESS != smfrc)
- {
- rc = XlatSMFErr(smfrc);
- goto Seq_Open_File_Cleanup;
- }
-
- pSeq->hSmf = sofs.hSmf;
- smfGetFileInfo(pSeq->hSmf, &sfi);
-
- pSeq->dwTimeDivision = sfi.dwTimeDivision;
- pSeq->tkLength = sfi.tkLength;
- pSeq->cTrk = sfi.dwTracks;
-
- /* Track buffers must be big enough to hold the state data returned
- ** by smfSeek()
- */
- cbBuffer = min(pSeq->cbBuffer, smfGetStateMaxSize());
-
- Seq_Open_File_Cleanup:
- if (MMSYSERR_NOERROR != rc)
- seqCloseFile(pSeq);
- else
- pSeq->uState = SEQ_S_OPENED;
-
- return rc;
- }
-
- /***************************************************************************
- *
- * seqCloseFile
- *
- * Deassociates a MIDI file with the given sequencer instance.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * stopped.
- *
- * A call to seqCloseFile must be paired with a prior call to
- * seqOpenFile. All buffers associated with the file will be
- * freed and the file will be closed. The sequencer must be
- * stopped before this call will be accepted.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqCloseFile(
- PSEQ pSeq)
- {
- LPMIDIHDR lpmh;
-
- assert(pSeq != NULL);
-
- if (SEQ_S_OPENED != pSeq->uState)
- return MCIERR_UNSUPPORTED_FUNCTION;
-
- if ((HSMF)NULL != pSeq->hSmf)
- {
- smfCloseFile(pSeq->hSmf);
- pSeq->hSmf = (HSMF)NULL;
- }
-
- /* If we were prerolled, need to clean up -- have an open MIDI handle
- ** and buffers in the ready queue
- */
-
- for (lpmh = pSeq->lpmhFree; lpmh; lpmh = lpmh->lpNext)
- midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh));
-
- if (pSeq->lpmhPreroll)
- midiOutUnprepareHeader(pSeq->hmidi, pSeq->lpmhPreroll, sizeof(*pSeq->lpmhPreroll));
-
- if (pSeq->hmidi != NULL)
- {
- midiStreamClose(pSeq->hmidi);
- pSeq->hmidi = NULL;
- }
-
- pSeq->uState = SEQ_S_NOFILE;
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqPreroll
- *
- * Prepares the file for playback at the given position.
- *
- * pSeq - The sequencer instance.
- *
- * lpPreroll - Specifies the starting and ending tick
- * positions to play between.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * opened or prerolled.
- *
- * Open the device so we can initialize channels.
- *
- * Loop through the tracks. For each track, seek to the given position and
- * send the init data SMF gives us to the handle.
- *
- * Wait for all init buffers to finish.
- *
- * Unprepare the buffers (they're only ever sent here; the sequencer
- * engine merges them into a single stream during normal playback) and
- * refill them with the first chunk of data from the track.
- *
- *
- ****************************************************************************/
- MMRESULT FNLOCAL seqPreroll(
- PSEQ pSeq,
- LPPREROLL lpPreroll)
- {
- SMFRESULT smfrc;
- MMRESULT mmrc = MMSYSERR_NOERROR;
- MIDIPROPTIMEDIV mptd;
- LPMIDIHDR lpmh = NULL;
- LPMIDIHDR lpmhPreroll = NULL;
- DWORD cbPrerollBuffer;
- UINT uDeviceID;
-
- assert(pSeq != NULL);
-
- pSeq->mmrcLastErr = MMSYSERR_NOERROR;
-
- if (pSeq->uState != SEQ_S_OPENED &&
- pSeq->uState != SEQ_S_PREROLLED)
- return MCIERR_UNSUPPORTED_FUNCTION;
-
- pSeq->tkBase = lpPreroll->tkBase;
- pSeq->tkEnd = lpPreroll->tkEnd;
-
- if (pSeq->hmidi)
- {
- // Recollect buffers from MMSYSTEM back into free queue
- //
- pSeq->uState = SEQ_S_RESET;
- midiOutReset(pSeq->hmidi);
-
- while (pSeq->uBuffersInMMSYSTEM)
- Sleep(0);
- }
-
- pSeq->uBuffersInMMSYSTEM = 0;
- pSeq->uState = SEQ_S_PREROLLING;
-
- //
- // We've successfully opened the file and all of the tracks; now
- // open the MIDI device and set the time division.
- //
- // NOTE: seqPreroll is equivalent to seek; device might already be open
- //
- if (NULL == pSeq->hmidi)
- {
- uDeviceID = pSeq->uDeviceID;
- if ((mmrc = midiStreamOpen(&pSeq->hmidi,
- &uDeviceID,
- 1,
- (DWORD)seqMIDICallback,
- 0,
- CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
- {
- pSeq->hmidi = NULL;
- goto seq_Preroll_Cleanup;
- }
-
- mptd.cbStruct = sizeof(mptd);
- mptd.dwTimeDiv = pSeq->dwTimeDivision;
- if ((mmrc = midiStreamProperty(
- (HMIDI)pSeq->hmidi,
- (LPBYTE)&mptd,
- MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR)
- {
- DPF(1, "midiStreamProperty() -> %04X", (WORD)mmrc);
- midiStreamClose(pSeq->hmidi);
- pSeq->hmidi = NULL;
- mmrc = MCIERR_DEVICE_NOT_READY;
- goto seq_Preroll_Cleanup;
- }
- }
-
- mmrc = MMSYSERR_NOERROR;
-
- //
- // Allocate a preroll buffer. Then if we don't have enough room for
- // all the preroll info, we make the buffer larger.
- //
- if (!pSeq->lpmhPreroll)
- {
- cbPrerollBuffer = 4096;
- lpmhPreroll = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE,
- cbPrerollBuffer);
- }
- else
- {
- cbPrerollBuffer = pSeq->cbPreroll;
- lpmhPreroll = pSeq->lpmhPreroll;
- }
-
- lpmhPreroll->lpNext = pSeq->lpmhFree;
- lpmhPreroll->lpData = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
- lpmhPreroll->dwBufferLength = cbPrerollBuffer - sizeof(MIDIHDR);
- lpmhPreroll->dwBytesRecorded = 0;
- lpmhPreroll->dwUser = (DWORD)(UINT)pSeq;
- lpmhPreroll->dwFlags = 0;
-
- do
- {
- smfrc = smfSeek(pSeq->hSmf, pSeq->tkBase, lpmhPreroll);
- if( SMF_SUCCESS != smfrc )
- {
- if( ( SMF_NO_MEMORY != smfrc ) ||
- ( cbPrerollBuffer >= 32768L ) )
- {
- DPF(1, "smfSeek() returned %lu", (DWORD)smfrc);
-
- GlobalFreePtr(lpmhPreroll);
- pSeq->lpmhPreroll = NULL;
-
- mmrc = XlatSMFErr(smfrc);
- goto seq_Preroll_Cleanup;
- }
- else // Try to grow buffer.
- {
- cbPrerollBuffer *= 2;
- lpmh = (LPMIDIHDR)GlobalReAllocPtr( lpmhPreroll, cbPrerollBuffer, 0 );
- if( NULL == lpmh )
- {
- DPF(2,"seqPreroll - realloc failed, aborting preroll.");
- mmrc = MCIERR_OUT_OF_MEMORY;
- goto seq_Preroll_Cleanup;
- }
-
- lpmhPreroll = lpmh;
- lpmhPreroll->lpData = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
- lpmhPreroll->dwBufferLength = cbPrerollBuffer - sizeof(MIDIHDR);
-
- pSeq->lpmhPreroll = lpmhPreroll;
- pSeq->cbPreroll = cbPrerollBuffer;
- }
- }
- } while( SMF_SUCCESS != smfrc );
-
- if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
- {
- DPF(1, "midiOutPrepare(preroll) -> %lu!", (DWORD)mmrc);
-
- mmrc = MCIERR_DEVICE_NOT_READY;
- goto seq_Preroll_Cleanup;
- }
-
- ++pSeq->uBuffersInMMSYSTEM;
-
- if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
- {
- DPF(1, "midiStreamOut(preroll) -> %lu!", (DWORD)mmrc);
-
- mmrc = MCIERR_DEVICE_NOT_READY;
- --pSeq->uBuffersInMMSYSTEM;
- goto seq_Preroll_Cleanup;
- }
- DPF(3,"seqPreroll: midiStreamOut(0x%x,0x%lx,%u) returned %u.",pSeq->hmidi,lpmhPreroll,sizeof(MIDIHDR),mmrc);
-
- pSeq->fdwSeq &= ~SEQ_F_EOF;
- while (pSeq->lpmhFree)
- {
- lpmh = pSeq->lpmhFree;
- pSeq->lpmhFree = lpmh->lpNext;
-
- smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
- if (SMF_SUCCESS != smfrc && SMF_END_OF_FILE != smfrc)
- {
- DPF(1, "SFP: smfReadEvents() -> %u", (UINT)smfrc);
- mmrc = XlatSMFErr(smfrc);
- goto seq_Preroll_Cleanup;
- }
-
- if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
- {
- DPF(1, "SFP: midiOutPrepareHeader failed");
- goto seq_Preroll_Cleanup;
- }
-
- if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh))))
- {
- DPF(1, "SFP: midiStreamOut failed");
- goto seq_Preroll_Cleanup;
- }
-
- ++pSeq->uBuffersInMMSYSTEM;
-
- if (SMF_END_OF_FILE == smfrc)
- {
- pSeq->fdwSeq |= SEQ_F_EOF;
- break;
- }
- }
-
- seq_Preroll_Cleanup:
- if (MMSYSERR_NOERROR != mmrc)
- {
- pSeq->uState = SEQ_S_OPENED;
- pSeq->fdwSeq &= ~SEQ_F_WAITING;
- }
- else
- {
- pSeq->uState = SEQ_S_PREROLLED;
- }
-
- return mmrc;
- }
-
- /***************************************************************************
- *
- * seqStart
- *
- * Starts playback at the current position.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * stopped.
- *
- * MCIERR_DEVICE_NOT_READY If the underlying MIDI device could
- * not be opened or fails any call.
- *
- * The sequencer must be prerolled before seqStart may be called.
- *
- * Just feed everything in the ready queue to the device.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqStart(
- PSEQ pSeq)
- {
- assert(NULL != pSeq);
-
- if (SEQ_S_PREROLLED != pSeq->uState)
- {
- DPF(1, "seqStart(): State is wrong! [%u]", pSeq->uState);
- return MCIERR_UNSUPPORTED_FUNCTION;
- }
-
- pSeq->uState = SEQ_S_PLAYING;
-
- return midiStreamRestart(pSeq->hmidi);
- }
-
- /***************************************************************************
- *
- * seqPause
- *
- * Pauses playback of the instance.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * playing.
- *
- * The sequencer must be playing before seqPause may be called.
- * Pausing the sequencer will cause all currently on notes to be turned
- * off. This may cause playback to be slightly inaccurate on restart
- * due to missing notes.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqPause(
- PSEQ pSeq)
- {
- assert(NULL != pSeq);
-
- if (SEQ_S_PLAYING != pSeq->uState)
- return MCIERR_UNSUPPORTED_FUNCTION;
-
- pSeq->uState = SEQ_S_PAUSED;
- midiStreamPause(pSeq->hmidi);
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqRestart
- *
- * Restarts playback of an instance after a pause.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * paused.
- *
- * The sequencer must be paused before seqRestart may be called.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqRestart(
- PSEQ pSeq)
- {
- assert(NULL != pSeq);
-
- if (SEQ_S_PAUSED != pSeq->uState)
- return MCIERR_UNSUPPORTED_FUNCTION;
-
- pSeq->uState = SEQ_S_PLAYING;
- midiStreamRestart(pSeq->hmidi);
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqStop
- *
- * Totally stops playback of an instance.
- *
- * pSeq - The sequencer instance.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * paused or playing.
- *
- * The sequencer must be paused or playing before seqStop may be called.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqStop(
- PSEQ pSeq)
- {
- assert(NULL != pSeq);
-
- /* Automatic success if we're already stopped
- */
- if (SEQ_S_PLAYING != pSeq->uState &&
- SEQ_S_PAUSED != pSeq->uState)
- {
- pSeq->fdwSeq &= ~SEQ_F_WAITING;
- return MMSYSERR_NOERROR;
- }
-
- pSeq->uState = SEQ_S_STOPPING;
- pSeq->fdwSeq |= SEQ_F_WAITING;
-
- if (MMSYSERR_NOERROR != (pSeq->mmrcLastErr = midiStreamStop(pSeq->hmidi)))
- {
- DPF(1, "midiOutStop() returned %lu in seqStop()!", (DWORD)pSeq->mmrcLastErr);
-
- pSeq->fdwSeq &= ~SEQ_F_WAITING;
- return MCIERR_DEVICE_NOT_READY;
- }
-
- while (pSeq->uBuffersInMMSYSTEM)
- Sleep(0);
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqTime
- *
- * Determine the current position in playback of an instance.
- *
- * pSeq - The sequencer instance.
- *
- * pTicks - A pointer to a DWORD where the current position
- * in ticks will be returned.
- *
- * Returns
- * MMSYSERR_NOERROR If the operation is successful.
- *
- * MCIERR_DEVICE_NOT_READY If the underlying device fails to report
- * the position.
- *
- * MCIERR_UNSUPPORTED_FUNCTION If the sequencer instance is not
- * paused or playing.
- *
- * The sequencer must be paused, playing or prerolled before seqTime
- * may be called.
- *
- ***************************************************************************/
- MMRESULT FNLOCAL seqTime(
- PSEQ pSeq,
- PTICKS pTicks)
- {
- MMRESULT mmr;
- MMTIME mmt;
-
- assert(pSeq != NULL);
-
- if (SEQ_S_PLAYING != pSeq->uState &&
- SEQ_S_PAUSED != pSeq->uState &&
- SEQ_S_PREROLLING != pSeq->uState &&
- SEQ_S_PREROLLED != pSeq->uState &&
- SEQ_S_OPENED != pSeq->uState)
- {
- DPF(1, "seqTime(): State wrong! [is %u]", pSeq->uState);
- return MCIERR_UNSUPPORTED_FUNCTION;
- }
-
- *pTicks = 0;
- if (SEQ_S_OPENED != pSeq->uState)
- {
- *pTicks = pSeq->tkBase;
- if (SEQ_S_PREROLLED != pSeq->uState)
- {
- mmt.wType = TIME_TICKS;
- mmr = midiStreamPosition(pSeq->hmidi, &mmt, sizeof(mmt));
- if (MMSYSERR_NOERROR != mmr)
- {
- DPF(1, "midiStreamPosition() returned %lu", (DWORD)mmr);
- return MCIERR_DEVICE_NOT_READY;
- }
-
- *pTicks += mmt.u.ticks;
- }
- }
-
- return MMSYSERR_NOERROR;
- }
-
- /***************************************************************************
- *
- * seqMillisecsToTicks
- *
- * Given a millisecond offset in the output stream, returns the associated
- * tick position.
- *
- * pSeq - The sequencer instance.
- *
- * msOffset - The millisecond offset into the stream.
- *
- * Returns the number of ticks into the stream.
- *
- ***************************************************************************/
- TICKS FNLOCAL seqMillisecsToTicks(
- PSEQ pSeq,
- DWORD msOffset)
- {
- return smfMillisecsToTicks(pSeq->hSmf, msOffset);
- }
-
- /***************************************************************************
- *
- * seqTicksToMillisecs
- *
- * Given a tick offset in the output stream, returns the associated
- * millisecond position.
- *
- * pSeq - The sequencer instance.
- *
- * tkOffset - The tick offset into the stream.
- *
- * Returns the number of milliseconds into the stream.
- *
- ***************************************************************************/
- DWORD FNLOCAL seqTicksToMillisecs(
- PSEQ pSeq,
- TICKS tkOffset)
- {
- return smfTicksToMillisecs(pSeq->hSmf, tkOffset);
- }
-
- /***************************************************************************
- *
- * seqMIDICallback
- *
- * Called by the system when a buffer is done
- *
- * dw1 - The buffer that has completed playback.
- *
- ***************************************************************************/
- PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
- {
- LPMIDIHDR lpmh = (LPMIDIHDR)dw1;
- PSEQ pSeq;
- MMRESULT mmrc;
- SMFRESULT smfrc;
-
- if (uMsg != MOM_DONE)
- return;
-
- assert(NULL != lpmh);
-
- pSeq = (PSEQ)(lpmh->dwUser);
-
- assert(pSeq != NULL);
-
- --pSeq->uBuffersInMMSYSTEM;
-
- if (SEQ_S_RESET == pSeq->uState)
- {
- // We're recollecting buffers from MMSYSTEM
- //
- if (lpmh != pSeq->lpmhPreroll)
- {
- lpmh->lpNext = pSeq->lpmhFree;
- pSeq->lpmhFree = lpmh;
- }
-
- return;
- }
-
-
- if ((SEQ_S_STOPPING == pSeq->uState) || (pSeq->fdwSeq & SEQ_F_EOF))
- {
- /*
- ** Reached EOF, just put the buffer back on the free
- ** list
- */
- if (lpmh != pSeq->lpmhPreroll)
- {
- lpmh->lpNext = pSeq->lpmhFree;
- pSeq->lpmhFree = lpmh;
- }
-
- if (MMSYSERR_NOERROR != (mmrc = midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
- {
- DPF(1, "midiOutUnprepareHeader failed in seqBufferDone! (%lu)", (DWORD)mmrc);
- }
-
- if (0 == pSeq->uBuffersInMMSYSTEM)
- {
- DPF(1, "seqBufferDone: normal sequencer shutdown.");
-
- /* Totally done! Free device and notify.
- */
- midiStreamClose(pSeq->hmidi);
-
- pSeq->hmidi = NULL;
- pSeq->uState = SEQ_S_OPENED;
- pSeq->mmrcLastErr = MMSYSERR_NOERROR;
- pSeq->fdwSeq &= ~SEQ_F_WAITING;
-
- // lParam indicates whether or not to preroll again. Don't if we were explicitly
- // stopped.
- //
- PostMessage(pSeq->hWnd, MMSG_DONE, (WPARAM)pSeq, (LPARAM)(SEQ_S_STOPPING != pSeq->uState));
- }
- }
- else
- {
- /*
- ** Not EOF yet; attempt to fill another buffer
- */
- smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
-
- switch(smfrc)
- {
- case SMF_SUCCESS:
- break;
-
- case SMF_END_OF_FILE:
- pSeq->fdwSeq |= SEQ_F_EOF;
- smfrc = SMF_SUCCESS;
- break;
-
- default:
- DPF(1, "smfReadEvents returned %lu in callback!", (DWORD)smfrc);
- pSeq->uState = SEQ_S_STOPPING;
- break;
- }
-
- if (SMF_SUCCESS == smfrc)
- {
- ++pSeq->uBuffersInMMSYSTEM;
- mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh));
- if (MMSYSERR_NOERROR != mmrc)
- {
- DPF(1, "seqBufferDone(): midiStreamOut() returned %lu!", (DWORD)mmrc);
-
- --pSeq->uBuffersInMMSYSTEM;
- pSeq->uState = SEQ_S_STOPPING;
- }
- }
- }
- }
-
- /***************************************************************************
- *
- * XlatSMFErr
- *
- * Translates an error from the SMF layer into an appropriate MCI error.
- *
- * smfrc - The return code from any SMF function.
- *
- * Returns
- * A parallel error from the MCI error codes.
- *
- ***************************************************************************/
- PRIVATE MMRESULT FNLOCAL XlatSMFErr(
- SMFRESULT smfrc)
- {
- switch(smfrc)
- {
- case SMF_SUCCESS:
- return MMSYSERR_NOERROR;
-
- case SMF_NO_MEMORY:
- return MCIERR_OUT_OF_MEMORY;
-
- case SMF_INVALID_FILE:
- case SMF_OPEN_FAILED:
- case SMF_INVALID_TRACK:
- return MCIERR_INVALID_FILE;
-
- default:
- return MCIERR_UNSUPPORTED_FUNCTION;
- }
- }
-
-
-
-
-