mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
909 lines
21 KiB
C++
909 lines
21 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
The midi player device.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
RTCLASS(MIDP)
|
|
|
|
|
|
const long kdtsMinSlip = kdtsSecond / 30;
|
|
const long klwInfinite = klwMax;
|
|
|
|
|
|
/***************************************************************************
|
|
Midi output object.
|
|
***************************************************************************/
|
|
enum
|
|
{
|
|
fmidoNil = 0x0,
|
|
fmidoFirst = 0x1,
|
|
fmidoFastFwd = 0x2,
|
|
};
|
|
|
|
typedef class MIDO *PMIDO;
|
|
#define MIDO_PAR BASE
|
|
#define kclsMIDO 'MIDO'
|
|
class MIDO : public MIDO_PAR
|
|
{
|
|
RTCLASS_DEC
|
|
|
|
protected:
|
|
typedef HMIDIOUT HMO;
|
|
|
|
MUTX _mutx; // restricts access to member variables
|
|
HMO _hmo; // the output device
|
|
|
|
// system volume level - to be saved and restored. The volume we set
|
|
// is always relative to this
|
|
ulong _luVolSys;
|
|
|
|
long _vlmBase; // our current volume relative to _luVolSys.
|
|
long _vlm; // our current volume relative to _vlmBase
|
|
|
|
long _sii; // the sound that owns the _hmo
|
|
long _spr; // the priority of sound that owns the _hmo
|
|
|
|
bool _fRestart: 1; // whether the device needs reset
|
|
bool _fSetVol: 1; // whether the volume needs set
|
|
|
|
void _GetSysVol(void);
|
|
void _SetSysVol(ulong luVol);
|
|
void _SetSysVlm(void);
|
|
void _Reset(void);
|
|
|
|
public:
|
|
MIDO(void);
|
|
~MIDO(void);
|
|
|
|
void Suspend(bool fSuspend);
|
|
void SetVlm(long vlm);
|
|
long VlmCur(void);
|
|
|
|
bool FPlay(long sii, long spr, MIDEV *pmidev, long vlm, ulong grfmido);
|
|
void Transition(long siiOld, long siiNew, long sprNew);
|
|
void Close(long sii);
|
|
};
|
|
|
|
|
|
static MIDO _mido;
|
|
|
|
|
|
RTCLASS(MIDO)
|
|
|
|
/***************************************************************************
|
|
Constructor for the low level midi output device.
|
|
***************************************************************************/
|
|
MIDO::MIDO(void)
|
|
{
|
|
_hmo = hNil;
|
|
_sii = siiNil;
|
|
_luVolSys = (ulong)(-1);
|
|
_vlmBase = _vlm = kvlmFull;
|
|
_fSetVol = fFalse;
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the low level midi output device.
|
|
***************************************************************************/
|
|
MIDO::~MIDO(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mutx.Enter();
|
|
Suspend(fTrue);
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the system volume level.
|
|
***************************************************************************/
|
|
void MIDO::_GetSysVol(void)
|
|
{
|
|
Assert(hNil != _hmo, "calling _VlmGetSys with nil _hmo");
|
|
|
|
if (0 != midiOutGetVolume((uint)_hmo, &_luVolSys))
|
|
{
|
|
// failed - assume full volume
|
|
_luVolSys = (ulong)(-1);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the system volume level.
|
|
***************************************************************************/
|
|
void MIDO::_SetSysVol(ulong luVol)
|
|
{
|
|
Assert(hNil != _hmo, "calling _SetSysVol with nil _hmo");
|
|
midiOutSetVolume((uint)_hmo, luVol);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the system volume level from the current values of _vlm, _vlmBase
|
|
and _luVolSys. We set the system volume to the result of scaling
|
|
_luVolSys by _vlm and _vlmBase.
|
|
***************************************************************************/
|
|
void MIDO::_SetSysVlm(void)
|
|
{
|
|
ulong luVol;
|
|
|
|
luVol = LuVolScale(_luVolSys, _vlmBase);
|
|
luVol = LuVolScale(luVol, _vlm);
|
|
_SetSysVol(luVol);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reset the midi device. Assumes that the mutx is already ours.
|
|
***************************************************************************/
|
|
void MIDO::_Reset(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (hNil != _hmo)
|
|
{
|
|
// Reset channel pressure and pitch wheel on all channels
|
|
MIDEV midev;
|
|
long iv;
|
|
|
|
midiOutReset(_hmo);
|
|
|
|
for (iv = 0; iv < 16; iv++)
|
|
{
|
|
midev.lwSend = 0;
|
|
midev.rgbSend[0] = (byte)(0xD0 | iv);
|
|
midiOutShortMsg(_hmo, midev.lwSend);
|
|
|
|
midev.rgbSend[0] = (byte)(0xE0 | iv);
|
|
midev.rgbSend[2] = 0x40;
|
|
midiOutShortMsg(_hmo, midev.lwSend);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Release or grab the midi output device depending on fSuspend.
|
|
***************************************************************************/
|
|
void MIDO::Suspend(bool fSuspend)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mutx.Enter();
|
|
if (FPure(fSuspend) != (hNil == _hmo))
|
|
{
|
|
if (fSuspend)
|
|
{
|
|
// kill all notes
|
|
_Reset();
|
|
|
|
// restore the volume level and free the device
|
|
_SetSysVol(_luVolSys);
|
|
midiOutClose(_hmo);
|
|
_hmo = hNil;
|
|
}
|
|
else
|
|
{
|
|
if (MMSYSERR_NOERROR == midiOutOpen(&_hmo, MIDI_MAPPER,
|
|
0, 0, CALLBACK_NULL))
|
|
{
|
|
_GetSysVol();
|
|
}
|
|
else
|
|
{
|
|
_hmo = hNil;
|
|
PushErc(ercSndMidiDeviceBusy);
|
|
}
|
|
}
|
|
}
|
|
|
|
_fSetVol = _fRestart = fTrue;
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the master volume for the device.
|
|
***************************************************************************/
|
|
void MIDO::SetVlm(long vlm)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (vlm != _vlmBase)
|
|
{
|
|
_vlmBase = vlm;
|
|
_fSetVol = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the current master volume.
|
|
***************************************************************************/
|
|
long MIDO::VlmCur(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return _vlmBase;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Play the given midi event. Returns false iff the midi stream should be
|
|
started over from the beginning in fast forward mode.
|
|
***************************************************************************/
|
|
bool MIDO::FPlay(long sii, long spr, MIDEV *pmidev, long vlm, ulong grfmido)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pmidev);
|
|
|
|
// assume we don't have to restart
|
|
bool fRet = fTrue;
|
|
|
|
_mutx.Enter();
|
|
|
|
// see if this sound has higher priority than the current one
|
|
if (_sii == sii)
|
|
Assert(_spr == spr, 0);
|
|
else if (siiNil == _sii || spr >= _spr && (sii > _sii || spr > _spr))
|
|
{
|
|
// this sound is higher priority so play it.
|
|
_sii = sii;
|
|
_spr = spr;
|
|
_fRestart = fTrue;
|
|
}
|
|
|
|
// if this sound isn't the current one or the output we're deactivated
|
|
// just pretend we played the event
|
|
if (_sii != sii || hNil == _hmo)
|
|
goto LDone;
|
|
|
|
// If we need to restart, reset the device. If this is the first event
|
|
// in the stream, go ahead and play it - otherwise, return false to tell
|
|
// the client to restart.
|
|
if (_fRestart)
|
|
{
|
|
_Reset();
|
|
_fRestart = fFalse;
|
|
|
|
if (!(grfmido & fmidoFirst))
|
|
{
|
|
fRet = fFalse;
|
|
goto LDone;
|
|
}
|
|
}
|
|
|
|
// do fast forward filtering
|
|
if (grfmido & fmidoFastFwd)
|
|
{
|
|
// don't play notes or do other stuff that doesn't affect
|
|
// the (long term) device state.
|
|
switch (pmidev->rgbSend[0] & 0xF0)
|
|
{
|
|
default:
|
|
goto LDone;
|
|
|
|
case 0xB0: // control change
|
|
case 0xC0: // program change
|
|
case 0xD0: // channel pressure
|
|
case 0xF0: // special stuff
|
|
break;
|
|
}
|
|
}
|
|
|
|
// make sure the volume is set correctly
|
|
if (_fSetVol || _vlm != vlm)
|
|
{
|
|
_vlm = vlm;
|
|
_fSetVol = fFalse;
|
|
_SetSysVlm();
|
|
}
|
|
|
|
// finally, we can play the event
|
|
midiOutShortMsg(_hmo, pmidev->lwSend);
|
|
|
|
LDone:
|
|
_mutx.Leave();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
siiOld is being replaced by siiNew.
|
|
***************************************************************************/
|
|
void MIDO::Transition(long siiOld, long siiNew, long sprNew)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mutx.Enter();
|
|
if (_sii == siiOld && siiNil != siiOld)
|
|
{
|
|
_sii = siiNew;
|
|
_spr = sprNew;
|
|
_Reset();
|
|
}
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
sii is going away.
|
|
***************************************************************************/
|
|
void MIDO::Close(long sii)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mutx.Enter();
|
|
if (_sii == sii && siiNil != sii)
|
|
{
|
|
_sii = siiNil;
|
|
if (hNil != _hmo)
|
|
_Reset();
|
|
}
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Midi player queue.
|
|
***************************************************************************/
|
|
typedef class MPQUE *PMPQUE;
|
|
#define MPQUE_PAR SNQUE
|
|
#define kclsMPQUE 'mpqu'
|
|
class MPQUE : public MPQUE_PAR
|
|
{
|
|
RTCLASS_DEC
|
|
ASSERT
|
|
MARKMEM
|
|
|
|
protected:
|
|
HN _hevtQueue; // the queue event object - to signal new input
|
|
bool _fChanged; // also signals new input - for extra protection
|
|
HN _hth; // the thread handle
|
|
|
|
MUTX _mutx; // mutex to restrict access to member variables
|
|
MSTP _mstp; // midi stream parser
|
|
long _dtsSlip; // amount of time we've slipped by
|
|
long _sii; // id and priority of sound we're currently serving
|
|
long _spr;
|
|
long _vlm; // volume to play back at
|
|
MIDEV _midev; // current midi event
|
|
ulong _tsStart; // time current sound was started
|
|
ulong _grfmido; // options for midi output device
|
|
|
|
bool _fMidevValid: 1; // is _midev valid?
|
|
bool _fDone: 1; // should the thread terminate?
|
|
|
|
MPQUE(void);
|
|
|
|
virtual void _Enter(void);
|
|
virtual void _Leave(void);
|
|
|
|
virtual bool _FInit(void);
|
|
virtual PBACO _PbacoFetch(PRCA prca, CTG ctg, CNO cno);
|
|
virtual void _Queue(long isndinMin);
|
|
virtual void _PauseQueue(long isndinMin);
|
|
virtual void _ResumeQueue(long isndinMin);
|
|
|
|
static ulong __stdcall _ThreadProc(void *pv);
|
|
|
|
ulong _LuThread(void);
|
|
void _DoEvent(bool fRestart, long *pdtsWait);
|
|
bool _FGetEvt(void);
|
|
bool _FStartQueue(void);
|
|
void _PlayEvt(void);
|
|
|
|
|
|
public:
|
|
static PMPQUE PmpqueNew(void);
|
|
~MPQUE(void);
|
|
};
|
|
|
|
|
|
RTCLASS(MPQUE)
|
|
|
|
|
|
/***************************************************************************
|
|
MT: Constructor for a midi player queue.
|
|
***************************************************************************/
|
|
MPQUE::MPQUE(void)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
MP: Destructor for a midi player queue.
|
|
***************************************************************************/
|
|
MPQUE::~MPQUE(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (hNil != _hth)
|
|
{
|
|
// tell the thread to end and wait for it to finish
|
|
_fDone = fTrue;
|
|
SetEvent(_hevtQueue);
|
|
WaitForSingleObject(_hth, INFINITE);
|
|
}
|
|
|
|
_mutx.Enter();
|
|
|
|
if (hNil != _hevtQueue)
|
|
CloseHandle(_hevtQueue);
|
|
|
|
// clear the midi stream parser
|
|
_mstp.Init(pvNil);
|
|
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a MPQUE.
|
|
***************************************************************************/
|
|
void MPQUE::AssertValid(ulong grf)
|
|
{
|
|
_mutx.Enter();
|
|
|
|
MPQUE_PAR::AssertValid(0);
|
|
AssertPo(&_mstp, 0);
|
|
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the MPQUE.
|
|
***************************************************************************/
|
|
void MPQUE::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
MPQUE_PAR::MarkMem();
|
|
|
|
_mutx.Enter();
|
|
MarkMemObj(&_mstp);
|
|
_mutx.Leave();
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
MT: Static method to create a new midi player queue.
|
|
***************************************************************************/
|
|
PMPQUE MPQUE::PmpqueNew(void)
|
|
{
|
|
PMPQUE pmpque;
|
|
|
|
if (pvNil == (pmpque = NewObj MPQUE))
|
|
return pvNil;
|
|
|
|
if (!pmpque->_FInit())
|
|
ReleasePpo(&pmpque);
|
|
|
|
AssertNilOrPo(pmpque, 0);
|
|
return pmpque;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
MT: Initialize the midi queue.
|
|
***************************************************************************/
|
|
bool MPQUE::_FInit(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
ulong luThread;
|
|
|
|
if (!MPQUE_PAR::_FInit())
|
|
return fFalse;
|
|
|
|
// create an auto-reset event to signal that the midi stream at
|
|
// the head of the queue has changed.
|
|
_hevtQueue = CreateEvent(pvNil, fFalse, fFalse, pvNil);
|
|
if (hNil == _hevtQueue)
|
|
return fFalse;
|
|
|
|
// create the thread in a suspended state
|
|
_hth = CreateThread(pvNil, 1024, MPQUE::_ThreadProc, this,
|
|
CREATE_SUSPENDED, &luThread);
|
|
if (hNil == _hth)
|
|
return fFalse;
|
|
SetThreadPriority(_hth, THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
// set other members
|
|
_sii = siiNil;
|
|
|
|
// start the thread
|
|
ResumeThread(_hth);
|
|
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Enter the critical section protecting member variables.
|
|
***************************************************************************/
|
|
void MPQUE::_Enter(void)
|
|
{
|
|
_mutx.Enter();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Leave the critical section protecting member variables.
|
|
***************************************************************************/
|
|
void MPQUE::_Leave(void)
|
|
{
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
MT: Fetch the given sound chunk as a midi stream.
|
|
***************************************************************************/
|
|
PBACO MPQUE::_PbacoFetch(PRCA prca, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(prca, 0);
|
|
|
|
return prca->PbacoFetch(ctg, cno, &MIDS::FReadMids);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The element at the head of the queue changed, notify the thread.
|
|
***************************************************************************/
|
|
void MPQUE::_Queue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mutx.Enter();
|
|
|
|
if (_isndinCur == isndinMin)
|
|
{
|
|
// signal the thread that data changed
|
|
SetEvent(_hevtQueue);
|
|
_fChanged = fTrue;
|
|
}
|
|
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Pause the sound at the head of the queue.
|
|
***************************************************************************/
|
|
void MPQUE::_PauseQueue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
SNDIN sndin;
|
|
|
|
_mutx.Enter();
|
|
|
|
if (_isndinCur == isndinMin && _pglsndin->IvMac() > _isndinCur)
|
|
{
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
sndin.dtsStart = TsCurrentSystem() - _tsStart;
|
|
_pglsndin->Put(_isndinCur, &sndin);
|
|
|
|
_Queue(_isndinCur);
|
|
}
|
|
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Resume the sound at the head of the queue.
|
|
***************************************************************************/
|
|
void MPQUE::_ResumeQueue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_Queue(isndinMin);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
AT: Static method. Thread function for the midi thread object.
|
|
***************************************************************************/
|
|
ulong __stdcall MPQUE::_ThreadProc(void *pv)
|
|
{
|
|
PMPQUE pmpque = (PMPQUE)pv;
|
|
|
|
AssertPo(pmpque, 0);
|
|
|
|
return pmpque->_LuThread();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
AT: The midi playback thread.
|
|
***************************************************************************/
|
|
ulong MPQUE::_LuThread(void)
|
|
{
|
|
AssertThis(0);
|
|
bool fRestart;
|
|
long dtsWait = klwInfinite;
|
|
|
|
for (;;)
|
|
{
|
|
// wait until our time has expired or there is new data
|
|
fRestart = dtsWait > 0 &&
|
|
WAIT_TIMEOUT != WaitForSingleObject(_hevtQueue,
|
|
dtsWait == klwInfinite ? INFINITE : dtsWait);
|
|
|
|
// check to see if this thread should end
|
|
if (_fDone)
|
|
return 0;
|
|
|
|
_mutx.Enter();
|
|
if (_fChanged && !fRestart)
|
|
dtsWait = klwInfinite;
|
|
else
|
|
{
|
|
_fChanged = fFalse;
|
|
_DoEvent(fRestart, &dtsWait);
|
|
}
|
|
_mutx.Leave();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Called when it's time to send the next midi event or when the queue
|
|
has changed. Assumes the mutx is already checked out.
|
|
***************************************************************************/
|
|
void MPQUE::_DoEvent(bool fRestart, long *pdtsWait)
|
|
{
|
|
if (fRestart && !_FStartQueue())
|
|
*pdtsWait = klwInfinite;
|
|
else if (!_FGetEvt())
|
|
{
|
|
// we're done playing this tune, so start the next one
|
|
_isndinCur++;
|
|
*pdtsWait = _FStartQueue() ? 0 : klwInfinite;
|
|
}
|
|
else
|
|
{
|
|
// we have a valid midi event
|
|
*pdtsWait = (long)(_midev.ts - TsCurrentSystem());
|
|
if (*pdtsWait <= 0)
|
|
{
|
|
// go ahead and send it
|
|
if (*pdtsWait < -kdtsMinSlip && !(_grfmido & fmidoFastFwd))
|
|
{
|
|
_dtsSlip -= *pdtsWait;
|
|
*pdtsWait = 0;
|
|
}
|
|
_PlayEvt();
|
|
}
|
|
else
|
|
_grfmido &= ~fmidoFastFwd;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
AT: Start playing the sound at the head of the queue. Return non-zero
|
|
iff the queue wasn't empty. Note that the sound is left in the queue.
|
|
***************************************************************************/
|
|
bool MPQUE::_FStartQueue(void)
|
|
{
|
|
SNDIN sndin;
|
|
|
|
_mutx.Enter();
|
|
|
|
// set up the midi stream parser (_mstp).
|
|
for ( ; _isndinCur < _pglsndin->IvMac(); _isndinCur++)
|
|
{
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
AssertPo(sndin.pbaco, 0);
|
|
if (0 <= sndin.cactPause)
|
|
break;
|
|
}
|
|
|
|
if (_isndinCur < _pglsndin->IvMac() && 0 == sndin.cactPause)
|
|
{
|
|
// transition to the new tune
|
|
_mido.Transition(_sii, sndin.sii, sndin.spr);
|
|
|
|
_sii = sndin.sii;
|
|
_spr = sndin.spr;
|
|
_vlm = sndin.vlm;
|
|
_tsStart = TsCurrentSystem() - sndin.dtsStart;
|
|
_mstp.Init((PMIDS)sndin.pbaco, _tsStart);
|
|
_dtsSlip = TsCurrentSystem() - sndin.dtsStart - _tsStart;
|
|
_grfmido = fmidoNil;
|
|
_fMidevValid = fFalse;
|
|
|
|
if (sndin.dtsStart > 0)
|
|
_grfmido |= fmidoFastFwd;
|
|
if (sndin.pbaco != pvNil)
|
|
_grfmido |= fmidoFirst;
|
|
}
|
|
else
|
|
{
|
|
// close the old tune
|
|
_mido.Close(_sii);
|
|
_sii = siiNil;
|
|
|
|
sndin.pbaco = pvNil;
|
|
_mstp.Init(pvNil, 0);
|
|
_fMidevValid = fFalse;
|
|
}
|
|
|
|
_mutx.Leave();
|
|
|
|
return sndin.pbaco != pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
AT: Get the next event. Assumes we already have the mutex.
|
|
***************************************************************************/
|
|
bool MPQUE::_FGetEvt(void)
|
|
{
|
|
AssertThis(0);
|
|
ulong ts;
|
|
SNDIN sndin;
|
|
|
|
if (_fMidevValid)
|
|
return fTrue;
|
|
|
|
ts = kluMax;
|
|
while (_mstp.FGetEvent(&_midev))
|
|
{
|
|
_midev.ts += _dtsSlip;
|
|
|
|
// skip empty events
|
|
if (_midev.cb > 0)
|
|
{
|
|
_fMidevValid = fTrue;
|
|
return fTrue;
|
|
}
|
|
ts = _midev.ts;
|
|
}
|
|
|
|
// see if we should repeat the current midi stream
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
if (--sndin.cactPlay == 0)
|
|
return fFalse;
|
|
_pglsndin->Put(_isndinCur, &sndin);
|
|
|
|
_tsStart = TsCurrentSystem();
|
|
if (ts != kluMax && ts > _tsStart)
|
|
_tsStart = ts;
|
|
|
|
_mstp.Init((PMIDS)sndin.pbaco, _tsStart);
|
|
_dtsSlip = TsCurrentSystem() - _tsStart;
|
|
if (!_mstp.FGetEvent(&_midev))
|
|
{
|
|
// there's nothing in this midi stream
|
|
return fFalse;
|
|
}
|
|
_midev.ts += _dtsSlip;
|
|
_fMidevValid = fTrue;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
AT: Play the current event. Assumes we have the member mutex (_mutx).
|
|
***************************************************************************/
|
|
void MPQUE::_PlayEvt(void)
|
|
{
|
|
AssertThis(0);
|
|
Assert(_fMidevValid, 0);
|
|
|
|
if (!_mido.FPlay(_sii, _spr, &_midev, _vlm, _grfmido))
|
|
{
|
|
// restart the stream in fast forward mode
|
|
SNDIN sndin;
|
|
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
_mstp.Init((PMIDS)sndin.pbaco, _tsStart);
|
|
_grfmido |= fmidoFastFwd;
|
|
}
|
|
_fMidevValid = fFalse;
|
|
_grfmido &= ~fmidoFirst;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the midi player device.
|
|
***************************************************************************/
|
|
MIDP::MIDP(void)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the midi player device.
|
|
***************************************************************************/
|
|
MIDP::~MIDP(void)
|
|
{
|
|
_Suspend(fTrue);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create the midiplayer device.
|
|
***************************************************************************/
|
|
PMIDP MIDP::PmidpNew(void)
|
|
{
|
|
PMIDP pmidp;
|
|
|
|
if (pvNil == (pmidp = NewObj MIDP))
|
|
return pvNil;
|
|
|
|
if (!pmidp->_FInit())
|
|
ReleasePpo(&pmidp);
|
|
|
|
pmidp->_Suspend(!pmidp->_fActive || pmidp->_cactSuspend > 0);
|
|
|
|
AssertNilOrPo(pmidp, 0);
|
|
return pmidp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Allocate a new midi queue.
|
|
***************************************************************************/
|
|
PSNQUE MIDP::_PsnqueNew(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return MPQUE::PmpqueNew();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get or release the HMIDIOUT depending on fSuspend.
|
|
***************************************************************************/
|
|
void MIDP::_Suspend(bool fSuspend)
|
|
{
|
|
_mido.Suspend(fSuspend);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the volume.
|
|
***************************************************************************/
|
|
void MIDP::SetVlm(long vlm)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_mido.SetVlm(vlm);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the volume.
|
|
***************************************************************************/
|
|
long MIDP::VlmCur(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return _mido.VlmCur();
|
|
}
|
|
|