|
|
//
// Copyright (c) 1996-2001 Microsoft Corporation
// UMSynth.cpp : Implementation of CUserModeSynth
//
// READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// 4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX
//
// We disable this because we use exceptions and do *not* specify -GX (USE_NATIVE_EH in
// sources).
//
// The one place we use exceptions is around construction of objects that call
// InitializeCriticalSection. We guarantee that it is safe to use in this case with
// the restriction given by not using -GX (automatic objects in the call chain between
// throw and handler are not destructed). Turning on -GX buys us nothing but +10% to code
// size because of the unwind code.
//
// Any other use of exceptions must follow these restrictions or -GX must be turned on.
//
// READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
#pragma warning(disable:4530)
#include <objbase.h>
#include <mmsystem.h>
#include <dsoundp.h>
#include <ks.h>
#include "debug.h"
#include "UMSynth.h"
#include "dmusicc.h"
#include "dmusics.h"
#include "math.h"
#include "misc.h"
#include "dmksctrl.h"
#include "dsoundp.h" // For IDirectSoundSource
#include "..\shared\dmusiccp.h" // For class ids.
#include <dmusprop.h>
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
#include "..\shared\validate.h"
#if 0 // The following section will only take affect in the DDK sample.
// @@END_DDKSPLIT
#include "validate.h"
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.
#endif
// @@END_DDKSPLIT
extern long g_cComponent;
/////////////////////////////////////////////////////////////////////
// User mode registry helper
//
BOOL GetRegValueDword( LPCTSTR szRegPath, LPCTSTR szValueName, LPDWORD pdwValue) { HKEY hKeyOpen; DWORD dwType; DWORD dwCbData; LONG lResult; BOOL fReturn = FALSE;
assert(pdwValue);
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegPath, 0, KEY_QUERY_VALUE, &hKeyOpen );
if (lResult == ERROR_SUCCESS) { dwCbData = sizeof(DWORD);
lResult = RegQueryValueEx(hKeyOpen, szValueName, NULL, &dwType, (LPBYTE)pdwValue, &dwCbData);
if (lResult == ERROR_SUCCESS && dwType == REG_DWORD) { fReturn = TRUE; }
RegCloseKey( hKeyOpen ); }
return fReturn; }
DWORD GetTheCurrentTime() { static BOOL s_fFirstTime = TRUE; static LARGE_INTEGER s_liPerfFrequency; static BOOL s_fUsePerfCounter = FALSE; if (s_fFirstTime) { s_fFirstTime = FALSE; s_fUsePerfCounter = QueryPerformanceFrequency(&s_liPerfFrequency); s_liPerfFrequency.QuadPart /= 1000; } if (s_fUsePerfCounter) { LARGE_INTEGER liPerfCounter; QueryPerformanceCounter(&liPerfCounter); liPerfCounter.QuadPart /= s_liPerfFrequency.QuadPart; return (DWORD) liPerfCounter.QuadPart; } else { return timeGetTime(); } }
/////////////////////////////////////////////////////////////////////////////
// CUserModeSynth
HRESULT CUserModeSynth::Init() { return S_OK; }
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
HRESULT CUserModeSynth::UseDefaultSynthSink() { HRESULT hr = S_OK; if (!m_pSynthSink) { IDirectMusicSynthSink *pSink = NULL; hr = CoCreateInstance(CLSID_DirectMusicSynthSink, NULL, CLSCTX_INPROC_SERVER, IID_IDirectMusicSynthSink, (void **) &pSink); if (pSink) { SetSynthSink(pSink); pSink->Release(); } } return hr; } // @@END_DDKSPLIT
CUserModeSynth::CUserModeSynth() { InterlockedIncrement(&g_cComponent);
m_fCSInitialized = FALSE; ::InitializeCriticalSection(&m_CriticalSection); // Note: on pre-Blackcomb OS's, this call can raise an exception; if it
// ever pops in stress, we can add an exception handler and retry loop.
m_fCSInitialized = TRUE;
m_cRef = 0; m_dwSampleRate = 22050; m_dwChannels = 2; m_lVolume = 0; m_lBoost = 6 * 100; m_lGainAdjust = 6 * 100; // Default 6 dB boost
m_fActive = FALSE; m_pSynth = NULL; m_pSynthSink = NULL; m_pSynthSink8 = NULL; m_ullPosition = 0; m_dwBufferFlags = BUFFERFLAG_INTERLEAVED; }
CUserModeSynth::~CUserModeSynth() { Activate(FALSE);
if (m_fCSInitialized) { ::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { delete m_pSynth; m_pSynth = NULL; }
if (m_pSynthSink) { m_pSynthSink->Release(); }
if (m_pSynthSink8) { m_pSynthSink8->Release(); }
::LeaveCriticalSection(&m_CriticalSection); ::DeleteCriticalSection(&m_CriticalSection); }
InterlockedDecrement(&g_cComponent); }
// CUserModeSynth::QueryInterface
//
STDMETHODIMP CUserModeSynth::QueryInterface(const IID &iid, void **ppv) { V_INAME(IDirectMusicSynth::QueryInterface); V_REFGUID(iid); V_PTRPTR_WRITE(ppv);
if (iid == IID_IUnknown || iid == IID_IDirectMusicSynth) { *ppv = static_cast<IDirectMusicSynth*>(this); } else if (iid == IID_IKsControl) { *ppv = static_cast<IKsControl*>(this); } else if (iid == IID_IDirectMusicSynth8 ) { *ppv = static_cast<IDirectMusicSynth8*>(this); } else if (iid == IID_IDirectSoundSource) { *ppv = static_cast<IDirectSoundSource*>(this); } else { *ppv = NULL; return E_NOINTERFACE; }
reinterpret_cast<IUnknown*>(this)->AddRef(); return S_OK; }
// CUserModeSynth::AddRef
//
STDMETHODIMP_(ULONG) CUserModeSynth::AddRef() { return InterlockedIncrement(&m_cRef); }
// CUserModeSynth::Release
//
STDMETHODIMP_(ULONG) CUserModeSynth::Release() { if (!InterlockedDecrement(&m_cRef)) { delete this; return 0; }
return m_cRef; }
STDMETHODIMP CUserModeSynth::SetSynthSink( IDirectMusicSynthSink *pSynthSink) // <i IDirectMusicSynthSink> to connect to synth, or
// NULL to disconnect.
{ HRESULT hr = S_OK; V_INAME(IDirectMusicSynth::SetSynthSink); V_INTERFACE_OPT(pSynthSink);
::EnterCriticalSection(&m_CriticalSection);
//>>>>>>>>. RELEASE THE DSINK IF PRESENT !!!!
if (m_pSynthSink) { hr = m_pSynthSink->Init(NULL); m_pSynthSink->Release(); }
m_pSynthSink = pSynthSink;
//>>>>>>>>> the current state of the format of the the synth is
//>>>>>>>>> ambiguos if a sink has been previously applied.
m_dwBufferFlags &= ~BUFFERFLAG_MULTIBUFFER; // .... just in case
if (m_pSynthSink) { m_pSynthSink->AddRef(); hr = m_pSynthSink->Init(static_cast<IDirectMusicSynth*>(this)); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::Open( LPDMUS_PORTPARAMS pPortParams) // <t DMUS_PORTPARAMS> structure for opening the port. If NULL, default settings are used.
{ V_INAME(IDirectMusicSynth::Open); //if (pPortParams == NULL)
//{
// Trace(1, "Error: Open called with NULL PortParams.\n");
// return E_FAIL;
//}
DWORD cbPortParams = 0; DWORD dwVer;
if (pPortParams) { V_STRUCTPTR_READ_VER(pPortParams, dwVer); V_STRUCTPTR_READ_VER_CASE(DMUS_PORTPARAMS, 7); V_STRUCTPTR_READ_VER_CASE(DMUS_PORTPARAMS, 8); V_STRUCTPTR_READ_VER_END(DMUS_PORTPARAMS, pPortParams);
switch (dwVer) { case 7: cbPortParams = sizeof(DMUS_PORTPARAMS7); break;
case 8: cbPortParams = sizeof(DMUS_PORTPARAMS8); break; } }
bool bPartialOpen = false;
DMUS_PORTPARAMS myParams; myParams.dwSize = sizeof (myParams); myParams.dwVoices = 32; myParams.dwChannelGroups = 2; myParams.dwAudioChannels = 2; myParams.dwSampleRate = 22050; #ifdef REVERB_ENABLED
myParams.dwEffectFlags = DMUS_EFFECT_REVERB; #else
myParams.dwEffectFlags = DMUS_EFFECT_NONE; #endif
myParams.fShare = FALSE; myParams.dwValidParams = DMUS_PORTPARAMS_VOICES | DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS | DMUS_PORTPARAMS_SAMPLERATE | DMUS_PORTPARAMS_EFFECTS | DMUS_PORTPARAMS_SHARE;
if (pPortParams) { if (pPortParams->dwSize >= sizeof(DMUS_PORTPARAMS8)) { myParams.dwValidParams |= DMUS_PORTPARAMS_FEATURES; myParams.dwFeatures = 0; } if (pPortParams->dwValidParams & DMUS_PORTPARAMS_VOICES) { if (pPortParams->dwVoices) { if (pPortParams->dwVoices <= MAX_VOICES) { myParams.dwVoices = pPortParams->dwVoices; } else { bPartialOpen = true; myParams.dwVoices = MAX_VOICES; } } else { bPartialOpen = true; myParams.dwVoices = 1; // MIN_VOICES
} } if (pPortParams->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS) { if (pPortParams->dwChannelGroups) { if (pPortParams->dwChannelGroups <= MAX_CHANNEL_GROUPS) { myParams.dwChannelGroups = pPortParams->dwChannelGroups; } else { bPartialOpen = true; myParams.dwChannelGroups = MAX_CHANNEL_GROUPS; } } else { bPartialOpen = true; myParams.dwChannelGroups = 1; // MIN_CHANNEL_GROUPS
} } if (pPortParams->dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS) { if (pPortParams->dwAudioChannels) { if (pPortParams->dwAudioChannels <= 2) { myParams.dwAudioChannels = pPortParams->dwAudioChannels; } else { bPartialOpen = true; myParams.dwAudioChannels = 2; // MAX_AUDIO_CHANNELS
} } else { bPartialOpen = true; myParams.dwAudioChannels = 1; // MIN_AUDIO_CHANNELS
} } if (pPortParams->dwValidParams & DMUS_PORTPARAMS_SAMPLERATE) { if (dwVer == 7) { // DX-7 compat: clamp sample rate to one of the
// understood rates.
//
if (pPortParams->dwSampleRate > 30000) { if(pPortParams->dwSampleRate != 44100) { bPartialOpen = true; }
myParams.dwSampleRate = 44100; } else if (pPortParams->dwSampleRate > 15000) { if(pPortParams->dwSampleRate != 22050) { bPartialOpen = true; }
myParams.dwSampleRate = 22050; } else { if(pPortParams->dwSampleRate != 11025) { bPartialOpen = true; }
myParams.dwSampleRate = 11025; } } else { if (pPortParams->dwSampleRate > 96000) { bPartialOpen = true; myParams.dwSampleRate = 96000; } else if (pPortParams->dwSampleRate < 11025) { bPartialOpen = true; myParams.dwSampleRate = 11025; } else myParams.dwSampleRate = pPortParams->dwSampleRate; } } if (pPortParams->dwValidParams & DMUS_PORTPARAMS_EFFECTS) { if (pPortParams->dwEffectFlags & ~DMUS_EFFECT_REVERB) { bPartialOpen = true; pPortParams->dwEffectFlags &= DMUS_EFFECT_REVERB; }
#ifdef REVERB_ENABLED
myParams.dwEffectFlags = pPortParams->dwEffectFlags; #else
myParams.dwEffectFlags = DMUS_EFFECT_NONE; if (pPortParams->dwEffectFlags & DMUS_EFFECT_REVERB) { bPartialOpen = true; } #endif
} if (pPortParams->dwValidParams & DMUS_PORTPARAMS_SHARE) { if (pPortParams->fShare) { bPartialOpen = true; } }
if ((pPortParams->dwValidParams & DMUS_PORTPARAMS_FEATURES) && (pPortParams->dwSize >= sizeof(DMUS_PORTPARAMS8))) { myParams.dwFeatures = pPortParams->dwFeatures; } }
if (pPortParams) { DWORD dwSize = min(cbPortParams, myParams.dwSize);
memcpy(pPortParams, &myParams, dwSize); pPortParams->dwSize = dwSize; }
m_dwSampleRate = myParams.dwSampleRate; m_dwChannels = myParams.dwAudioChannels; m_dwBufferFlags = (m_dwChannels==1)?BUFFERFLAG_MONO:BUFFERFLAG_INTERLEAVED;
::EnterCriticalSection(&m_CriticalSection); HRESULT hr = DMUS_E_ALREADYOPEN; if (!m_pSynth) { try { m_pSynth = new CSynth; } catch( ... ) { m_pSynth = NULL; }
if (!m_pSynth) { hr = E_OUTOFMEMORY; } else { hr = m_pSynth->Open(myParams.dwChannelGroups, myParams.dwVoices, (myParams.dwEffectFlags & DMUS_EFFECT_REVERB) ? TRUE : FALSE); if (SUCCEEDED(hr)) { m_pSynth->SetGainAdjust(m_lGainAdjust); m_pSynth->Activate(m_dwSampleRate, m_dwBufferFlags); } else { delete m_pSynth; m_pSynth = NULL; } } } ::LeaveCriticalSection(&m_CriticalSection);
if(SUCCEEDED(hr)) { if(bPartialOpen) { hr = S_FALSE; } }
return hr; }
STDMETHODIMP CUserModeSynth::SetNumChannelGroups( DWORD dwGroups) // Number of ChannelGroups requested.
{ ::EnterCriticalSection(&m_CriticalSection); HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED; if (m_pSynth) { hr = m_pSynth->SetNumChannelGroups(dwGroups); } ::LeaveCriticalSection(&m_CriticalSection); return hr; }
STDMETHODIMP CUserModeSynth::Close() { ::EnterCriticalSection(&m_CriticalSection); HRESULT hr = DMUS_E_ALREADYCLOSED; if (m_pSynth) { hr = m_pSynth->Close(); delete m_pSynth; m_pSynth = NULL; } ::LeaveCriticalSection(&m_CriticalSection); return hr; } STDMETHODIMP CUserModeSynth::Download( LPHANDLE phDownload, // Pointer to download handle, to be created by <om IDirectMusicSynth::Download> and used later to unload the data.
LPVOID pvData, // Pointer to continuous memory segment with download data.
LPBOOL pbFree) // <p pbFree> indicates whether the synthesizer wishes to keep the memory in <p pvData> allocated.
{ HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED; V_INAME(IDirectMusicSynth::Download); V_PTR_WRITE(phDownload, HANDLE); V_PTR_WRITE(pbFree, BOOL);
// pvData is validated inside synth while parsing.
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->Download(phDownload, pvData, pbFree); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::Unload( HANDLE hDownload, // Handle to data, previously downloaded with a call to <om IDirectMusicSynth::Download>.
HRESULT ( CALLBACK *lpFreeHandle)(HANDLE, HANDLE), // If the original call to
// <om IDirectMusicSynth::Download> returned FALSE in <p pbFree>,
// the synthesizer hung onto the memory in the download chunk. If so,
// the caller must be notified once the memory has been freed,
// but that could occur later than <om IDirectMusicSynth::Download>
// since a wave might be currently in use. <p lpFreeHandle> is a
// pointer to a callback
// function which will be called when the memory is no longer in use.
HANDLE hUserData) // Pointer to user data, passed as a parameter to the
// <p lpFreeHandle> function, typically used so the callback routine can retrieve
// its state.
{ HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED; ::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->Unload(hDownload, lpFreeHandle, hUserData); } ::LeaveCriticalSection(&m_CriticalSection); return hr; }
STDMETHODIMP CUserModeSynth::PlayBuffer( REFERENCE_TIME rt, // Start time of the buffer. This should be in
// REFERENCE_TIME units, relative to the master
// clock, previously set with a call to <om IDirectMusicSynth::SetMasterClock>.
// And, this should be after the time returned by the clock in
// <om IDirectMusicSynth::GetLatencyClock>.
LPBYTE pbBuffer, // Memory chunk with all the MIDI events, generated by <i IDirectMusicBuffer>.
DWORD cbBuffer) // Size of buffer.
{ class MIDIEVENT : public DMUS_EVENTHEADER { public: BYTE abEvent[4]; /* Actual event data, rounded up to be an even number */ /* of QWORD's (8 bytes) */ };
typedef class MIDIEVENT FAR *LPMIDIEVENT; #define QWORD_ALIGN(x) (((x) + 7) & ~7)
HRESULT hr = DMUS_E_NOT_INIT;
V_INAME(IDirectMusicSynth::PlayBuffer); V_BUFPTR_READ(pbBuffer, cbBuffer);
::EnterCriticalSection(&m_CriticalSection);
if (!m_pSynthSink && !m_pSynthSink8) { ::LeaveCriticalSection(&m_CriticalSection); return DMUS_E_NOSYNTHSINK; }
if (!m_fActive) { ::LeaveCriticalSection(&m_CriticalSection); Trace(3, "Warning: Synth is inactive, can not process MIDI events.\n"); return DMUS_E_SYNTHINACTIVE; }
LPMIDIEVENT lpEventHdr; DWORD cbEvent;
while (cbBuffer) { if (cbBuffer < sizeof(DMUS_EVENTHEADER)) { Trace(1, "Error: PlayBuffer called with error in buffer size.\n"); ::LeaveCriticalSection(&m_CriticalSection); return E_INVALIDARG; }
lpEventHdr = (LPMIDIEVENT)pbBuffer; cbEvent = DMUS_EVENT_SIZE(lpEventHdr->cbEvent); if (cbEvent > cbBuffer) { Trace(1, "Error: PlayBuffer called with error in event size.\n"); ::LeaveCriticalSection(&m_CriticalSection); return E_INVALIDARG; }
pbBuffer += cbEvent; cbBuffer -= cbEvent; if ( m_pSynthSink ) { hr = m_pSynth->PlayBuffer(m_pSynthSink, rt + lpEventHdr->rtDelta, &lpEventHdr->abEvent[0], lpEventHdr->cbEvent, lpEventHdr->dwChannelGroup); }
if ( m_pSynthSink8 ) { hr = m_pSynth->PlayBuffer(m_pSynthSink8, rt + lpEventHdr->rtDelta, &lpEventHdr->abEvent[0], lpEventHdr->cbEvent, lpEventHdr->dwChannelGroup); }
if (FAILED(hr)) { ::LeaveCriticalSection(&m_CriticalSection); return hr; } } ::LeaveCriticalSection(&m_CriticalSection); return S_OK; }
STDMETHODIMP CUserModeSynth::GetPortCaps( LPDMUS_PORTCAPS pCaps) // <t DMUS_PORTCAPS> structure to be filled in by synth.
{ V_INAME(IDirectMusicSynth::GetPortCaps); V_STRUCTPTR_WRITE(pCaps, DMUS_PORTCAPS);
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
wcscpy(pCaps->wszDescription, L"Microsoft Synthesizer"); #if 0 // The following section will only take affect in the DDK sample.
// @@END_DDKSPLIT
wcscpy(pCaps->wszDescription, L"Microsoft DDK Synthesizer"); // @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.
#endif
// @@END_DDKSPLIT
pCaps->dwClass = DMUS_PC_OUTPUTCLASS; pCaps->dwType = DMUS_PORT_USER_MODE_SYNTH; pCaps->dwFlags = DMUS_PC_DLS | DMUS_PC_DLS2 | DMUS_PC_SOFTWARESYNTH | DMUS_PC_DIRECTSOUND | DMUS_PC_AUDIOPATH | DMUS_PC_WAVE;
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
pCaps->guidPort = CLSID_DirectMusicSynth; #if 0 // The following section will only take affect in the DDK sample.
// @@END_DDKSPLIT
pCaps->guidPort = CLSID_DDKSynth; // @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.
#endif
// @@END_DDKSPLIT
pCaps->dwMemorySize = DMUS_PC_SYSTEMMEMORY; pCaps->dwMaxChannelGroups = MAX_CHANNEL_GROUPS; pCaps->dwMaxVoices = MAX_VOICES; pCaps->dwMaxAudioChannels = 2;
pCaps->dwEffectFlags = 0; // @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
pCaps->dwEffectFlags = DMUS_EFFECT_REVERB; // @@END_DDKSPLIT
return S_OK; }
STDMETHODIMP CUserModeSynth::SetMasterClock( IReferenceClock *pClock) // Pointer to master <i IReferenceClock>,
// used by all devices in current instance of DirectMusic.
{ V_INAME(IDirectMusicSynth::SetMasterClock); V_INTERFACE(pClock);
return S_OK; }
STDMETHODIMP CUserModeSynth::GetLatencyClock( IReferenceClock **ppClock) // <i IReferenceClock> interface designed to return the current mix time.
{ IDirectSoundSynthSink* pDSSink = NULL;
V_INAME(IDirectMusicSynth::GetLatencyClock); V_PTR_WRITE(ppClock, IReferenceClock *);
HRESULT hr = DMUS_E_NOSYNTHSINK;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynthSink) { hr = m_pSynthSink->GetLatencyClock(ppClock); ::LeaveCriticalSection(&m_CriticalSection); } else if (m_pSynthSink8) { pDSSink = m_pSynthSink8; ::LeaveCriticalSection(&m_CriticalSection);
// FIXME:: The call to GetLatencyClock requres the DSound DLL Mutex and
// so we have to be outside of the Synth CriticalSection to make the call
// In theory, pDSSink could have been released by another thread at this point
//
// That happens if we get a simultaneous call to the destructor or to SetSink.
try { hr = pDSSink->GetLatencyClock(ppClock); } catch(...) { // If we're here the pointer to pDSSink has gone bad.
hr = E_UNEXPECTED; }
} else // still need to leave the critical section...
{ ::LeaveCriticalSection(&m_CriticalSection); }
return hr; }
STDMETHODIMP CUserModeSynth::Activate( BOOL fEnable) // Whether to activate or deactivate audio.
{ HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
// ::EnterCriticalSection(&m_CriticalSection);
if (fEnable) { if (m_pSynthSink || m_pSynthSink8) { if (!m_fActive) { if (m_dwSampleRate && m_dwChannels) { if (m_pSynth) { m_pSynth->Activate(m_dwSampleRate, m_dwBufferFlags);
if (m_pSynthSink) { if (SUCCEEDED(m_pSynthSink->Activate(fEnable))) { m_fActive = TRUE; hr = S_OK; } }
if ( m_pSynthSink8 ) { hr = m_pSynthSink8->Activate(fEnable); if (SUCCEEDED(hr) || hr == DMUS_E_SYNTHACTIVE) { m_fActive = TRUE; hr = S_OK; } } } } } else { Trace(1, "Error: Synth::Activate- synth already active\n"); hr = DMUS_E_SYNTHACTIVE; //>>>>>>>>>>>>>>>>>>>>> what's this about test it before removing????
hr = S_FALSE; } } else { Trace(1, "Error: Synth::Activate- sink not connected\n"); hr = DMUS_E_NOSYNTHSINK; } } else { if (m_fActive) { m_fActive = FALSE; if (m_pSynth) { m_pSynth->Deactivate(); }
if (m_pSynthSink) { if (SUCCEEDED(m_pSynthSink->Activate(fEnable))) { hr = S_OK; } }
if (m_pSynthSink8) { hr = m_pSynthSink8->Activate(fEnable); } } else { Trace(2, "Warning: Synth::Activate- synth already inactive\n"); hr = S_FALSE; } } // ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::Render( short *pBuffer, // Pointer to buffer to write into.
DWORD dwLength, // Length of buffer, in samples. This is not the
// memory size of the buffer. The memory size may vary,
// dependant on the buffer format, which the synth
// sets when in response to an <om IDirectMusicSynth::Activate>
// command.
LONGLONG llPosition) // Position in the audio stream, also in samples.
// This should always increment by <p dwLength> after
// each call.
{ V_INAME(IDirectMusicSynth::Render); V_BUFPTR_WRITE(pBuffer, dwLength << (m_dwBufferFlags&BUFFERFLAG_INTERLEAVED)?1:0 );
if (!m_pSynthSink) { Trace(1, "Error: Synth is not configured, can not render.\n"); return DMUS_E_SYNTHNOTCONFIGURED; } if (!m_fActive) { Trace(1, "Error: Synth is not inactive, can not render.\n"); return DMUS_E_SYNTHINACTIVE; }
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { DWORD dwID[2]; DWORD dwFuncID[2]; long lPitchBend[2];
// Setup busid for a Backward compatible DX7 interleaved buffer
dwID[0] = DSBUSID_LEFT; dwID[1] = DSBUSID_RIGHT; dwFuncID[0] = DSBUSID_LEFT; dwFuncID[1] = DSBUSID_RIGHT; lPitchBend[0] = lPitchBend[1] = 0;
DWORD dwChannels = 1; if (m_pSynth->m_dwStereo) { dwChannels = 2; } m_pSynth->Mix(&pBuffer, dwID, dwFuncID, lPitchBend, dwChannels, m_dwBufferFlags, dwLength, llPosition); } ::LeaveCriticalSection(&m_CriticalSection); return S_OK; }
STDMETHODIMP CUserModeSynth::SetChannelPriority( DWORD dwChannelGroup, DWORD dwChannel, DWORD dwPriority) { if (m_pSynth) { return m_pSynth->SetChannelPriority(dwChannelGroup, dwChannel, dwPriority); } Trace(1, "Error: Synth not initialized.\n"); return E_FAIL; }
STDMETHODIMP CUserModeSynth::GetChannelPriority( DWORD dwChannelGroup, DWORD dwChannel, LPDWORD pdwPriority) { if (m_pSynth) { return m_pSynth->GetChannelPriority(dwChannelGroup, dwChannel, pdwPriority); } Trace(1, "Error: Synth not initialized.\n"); return E_FAIL; }
// IDirectSoundSource version of GetFormat()
STDMETHODIMP CUserModeSynth::GetFormat( LPWAVEFORMATEX pWaveFormatEx, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) { V_INAME(IDirectMusicSynth::GetFormat);
if (!m_pSynth) { Trace(1, "Error: Synth is not configured, can not get format.\n"); return DMUS_E_SYNTHNOTCONFIGURED; }
if (!pWaveFormatEx && !pdwSizeWritten) { Trace(1, "Error: GetFormat failed, must request either the format or the required size"); return E_INVALIDARG; }
if (pdwSizeWritten) { V_PTR_WRITE(pdwSizeWritten, DWORD); *pdwSizeWritten = sizeof(WAVEFORMATEX); }
if (pWaveFormatEx) { V_BUFPTR_WRITE_OPT(pWaveFormatEx, dwSizeAllocated); WAVEFORMATEX wfx; memset(&wfx, 0, sizeof(wfx)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = (WORD)m_dwChannels; wfx.nSamplesPerSec = (WORD)m_dwSampleRate; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; // no extra data
memcpy(pWaveFormatEx, &wfx, min(sizeof wfx, dwSizeAllocated)); }
return S_OK; }
// IDirectMusicSynth8 version of GetFormat()
STDMETHODIMP CUserModeSynth::GetFormat( LPWAVEFORMATEX pWaveFormatEx, LPDWORD pdwWaveFormatExSize) { V_INAME(IDirectMusicSynth::GetFormat); V_PTR_WRITE(pdwWaveFormatExSize, DWORD); V_BUFPTR_WRITE_OPT(pWaveFormatEx, *pdwWaveFormatExSize); return GetFormat(pWaveFormatEx, *pdwWaveFormatExSize, pdwWaveFormatExSize); }
STDMETHODIMP CUserModeSynth::GetAppend( DWORD* pdwAppend) { V_INAME(IDirectMusicSynth::GetAppend); V_PTR_WRITE(pdwAppend, DWORD);
*pdwAppend = 2; // The synth needs 1 extra sample for loop interpolation.
// We're adding one more to be paranoid.
return S_OK; }
STDMETHODIMP CUserModeSynth::GetRunningStats( LPDMUS_SYNTHSTATS pStats) // <t DMUS_SYNTHSTATS> structure to fill in.
{ HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED; V_INAME(IDirectMusicSynth::GetRunningStats); V_STRUCTPTR_WRITE(pStats, DMUS_SYNTHSTATS); if ( pStats->dwSize == sizeof(DMUS_SYNTHSTATS8) ) { V_STRUCTPTR_WRITE(pStats, DMUS_SYNTHSTATS8); }
if (!m_pSynthSink && !m_pSynthSink8) { Trace(1, "Error: Synth::GetRunningStats failed because synth is inactove.\n"); return hr; }
if (m_fActive) { ::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { PerfStats Stats; m_pSynth->GetPerformanceStats(&Stats); long lCPU = Stats.dwCPU; if (Stats.dwVoices) { lCPU /= Stats.dwVoices; } else { lCPU = 0; } pStats->dwVoices = Stats.dwVoices; pStats->dwCPUPerVoice = lCPU * 10; pStats->dwTotalCPU = Stats.dwCPU * 10; pStats->dwLostNotes = Stats.dwNotesLost; long ldB = 6; double fLevel = Stats.dwMaxAmplitude; if (Stats.dwMaxAmplitude < 1) { fLevel = -96.0; } else { fLevel /= 32768.0; fLevel = log10(fLevel); fLevel *= 20.0; } pStats->lPeakVolume = (long) fLevel; pStats->dwValidStats = DMUS_SYNTHSTATS_VOICES | DMUS_SYNTHSTATS_TOTAL_CPU | DMUS_SYNTHSTATS_CPU_PER_VOICE | DMUS_SYNTHSTATS_LOST_NOTES | DMUS_SYNTHSTATS_PEAK_VOLUME;
if ( pStats->dwSize == sizeof(DMUS_SYNTHSTATS8) ) { ((DMUS_SYNTHSTATS8*)pStats)->dwSynthMemUse = m_pSynth->m_Instruments.m_dwSynthMemUse; }
hr = S_OK; } ::LeaveCriticalSection(&m_CriticalSection); } else { DWORD dwSize = pStats->dwSize; memset(pStats, 0, dwSize); pStats->dwSize = dwSize;
hr = S_OK; } return hr; }
static DWORD dwPropFalse = FALSE; static DWORD dwPropTrue = TRUE; static DWORD dwSystemMemory = DMUS_PC_SYSTEMMEMORY;
GENERICPROPERTY CUserModeSynth::m_aProperty[] = { { &GUID_DMUS_PROP_GM_Hardware, // Set
0, // Item
KSPROPERTY_SUPPORT_GET, // KS support flags
GENPROP_F_STATIC, // GENPROP flags
&dwPropFalse, sizeof(dwPropFalse), // static data and size
NULL // Handler
}, { &GUID_DMUS_PROP_GS_Hardware, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropFalse, sizeof(dwPropFalse), NULL }, { &GUID_DMUS_PROP_XG_Hardware, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropFalse, sizeof(dwPropFalse), NULL }, { &GUID_DMUS_PROP_XG_Capable, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropTrue, sizeof(dwPropTrue), NULL }, { &GUID_DMUS_PROP_GS_Capable, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropTrue, sizeof(dwPropTrue), NULL }, { &GUID_DMUS_PROP_INSTRUMENT2, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropTrue, sizeof(dwPropTrue), NULL }, { &GUID_DMUS_PROP_DLS1, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropTrue, sizeof(dwPropTrue), NULL }, { &GUID_DMUS_PROP_DLS2, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwPropTrue, sizeof(dwPropTrue), NULL }, { &GUID_DMUS_PROP_SampleMemorySize, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_STATIC, &dwSystemMemory, sizeof(dwSystemMemory), NULL }, { &KSPROPSETID_Synth, KSPROPERTY_SYNTH_VOLUME, KSPROPERTY_SUPPORT_SET, GENPROP_F_FNHANDLER, NULL, 0, CUserModeSynth::HandleSetVolume }, { &KSPROPSETID_Synth, KSPROPERTY_SYNTH_VOLUMEBOOST, KSPROPERTY_SUPPORT_SET, GENPROP_F_FNHANDLER, NULL, 0, CUserModeSynth::HandleSetBoost }, { &GUID_DMUS_PROP_WavesReverb, 0, KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET, GENPROP_F_FNHANDLER, NULL, 0, CUserModeSynth::HandleReverb }, { &GUID_DMUS_PROP_Effects, 0, KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET, GENPROP_F_FNHANDLER, NULL, 0, CUserModeSynth::HandleEffects }, { &GUID_DMUS_PROP_SamplePlaybackRate, 0, KSPROPERTY_SUPPORT_GET, GENPROP_F_FNHANDLER, NULL, 0, CUserModeSynth::HandleGetSampleRate } };
const int CUserModeSynth::m_nProperty = sizeof(m_aProperty) / sizeof(m_aProperty[0]);
HRESULT CUserModeSynth::HandleGetSampleRate( ULONG ulId, BOOL fSet, LPVOID pbBuffer, PULONG pcbBuffer) { if (*pcbBuffer != sizeof(LONG)) { return E_INVALIDARG; } if (!fSet) { *(long*)pbBuffer = m_dwSampleRate; } return S_OK; }
HRESULT CUserModeSynth::HandleSetVolume( ULONG ulId, BOOL fSet, LPVOID pbBuffer, PULONG pcbBuffer) { if (*pcbBuffer != sizeof(LONG)) { return E_INVALIDARG; }
m_lVolume = *(LONG*)pbBuffer; m_lGainAdjust = m_lVolume + m_lBoost;
if (m_pSynth) { m_pSynth->SetGainAdjust(m_lGainAdjust); } return S_OK; }
HRESULT CUserModeSynth::HandleSetBoost( ULONG ulId, BOOL fSet, LPVOID pbBuffer, PULONG pcbBuffer) { if (*pcbBuffer != sizeof(LONG)) { return E_INVALIDARG; }
m_lBoost = *(LONG*)pbBuffer; m_lGainAdjust = m_lVolume + m_lBoost;
if (m_pSynth) { m_pSynth->SetGainAdjust(m_lGainAdjust); } return S_OK; }
HRESULT CUserModeSynth::HandleReverb(ULONG ulId, BOOL fSet, LPVOID pbBuffer, PULONG pcbBuffer) { DMUS_WAVES_REVERB_PARAMS *pParams; if (*pcbBuffer != sizeof(DMUS_WAVES_REVERB_PARAMS)) { return E_INVALIDARG; }
pParams = (DMUS_WAVES_REVERB_PARAMS *) pbBuffer; if (m_pSynth) { if (fSet) { m_pSynth->SetReverb(pParams); } else { m_pSynth->GetReverb(pParams); } }
return S_OK; }
HRESULT CUserModeSynth::HandleEffects( ULONG ulId, BOOL fSet, LPVOID pbBuffer, PULONG pcbBuffer) { if (*pcbBuffer != sizeof(LONG)) { return E_INVALIDARG; } if (fSet) { long lEffects = *(long*)pbBuffer;
if (m_pSynth) { m_pSynth->SetReverbActive(lEffects & DMUS_EFFECT_REVERB); } } else { if (m_pSynth && m_pSynth->IsReverbActive()) { *(long*)pbBuffer = DMUS_EFFECT_REVERB; } else { *(long*)pbBuffer = 0; } } return S_OK; }
//
// CDirectMusicEmulatePort::FindPropertyItem
//
// Given a GUID and an item ID, find the associated property item in the synth's
// table of SYNPROPERTY's.
//
// Returns a pointer to the entry or NULL if the item was not found.
//
GENERICPROPERTY *CUserModeSynth::FindPropertyItem(REFGUID rguid, ULONG ulId) { GENERICPROPERTY *pPropertyItem = &m_aProperty[0]; GENERICPROPERTY *pEndOfItems = pPropertyItem + m_nProperty;
// Special Case -- We don't support Waves Reverb on a SinthSink8
if ((rguid == GUID_DMUS_PROP_WavesReverb) && (this->m_pSynthSink8 != NULL)) return NULL;
for (; pPropertyItem != pEndOfItems; pPropertyItem++) { if (*pPropertyItem->pguidPropertySet == rguid && pPropertyItem->ulId == ulId) { return pPropertyItem; } }
return NULL; }
#define KS_VALID_FLAGS (KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET| KSPROPERTY_TYPE_BASICSUPPORT)
STDMETHODIMP CUserModeSynth::KsProperty( PKSPROPERTY pPropertyIn, ULONG ulPropertyLength, LPVOID pvPropertyData, ULONG ulDataLength, PULONG pulBytesReturned) { V_INAME(DirectMusicSynthPort::IKsContol::KsProperty); V_BUFPTR_WRITE(pPropertyIn, ulPropertyLength);
DWORD dwFlags = pPropertyIn->Flags & KS_VALID_FLAGS;
switch (dwFlags) { case KSPROPERTY_TYPE_GET: V_BUFPTR_WRITE_OPT(pvPropertyData, ulDataLength); break;
case KSPROPERTY_TYPE_SET: V_BUFPTR_READ(pvPropertyData, ulDataLength); break;
case KSPROPERTY_TYPE_BASICSUPPORT: V_BUFPTR_WRITE(pvPropertyData, ulDataLength); break; }
V_PTR_WRITE(pulBytesReturned, ULONG);
GENERICPROPERTY *pProperty = FindPropertyItem(pPropertyIn->Set, pPropertyIn->Id);
if (pProperty == NULL) { Trace(2, "Warning: KsProperty call requested unknown property.\n"); return DMUS_E_UNKNOWN_PROPERTY; }
switch (dwFlags) { case KSPROPERTY_TYPE_GET: if (!(pProperty->ulSupported & KSPROPERTY_SUPPORT_GET)) { Trace(1, "Error: SynthSink does not support Get for the requested property.\n"); return DMUS_E_GET_UNSUPPORTED; }
if (pProperty->ulFlags & GENPROP_F_FNHANDLER) { GENPROPHANDLER pfn = pProperty->pfnHandler; *pulBytesReturned = ulDataLength; return (this->*pfn)(pPropertyIn->Id, FALSE, pvPropertyData, pulBytesReturned); }
if (ulDataLength > pProperty->cbPropertyData) { ulDataLength = pProperty->cbPropertyData; }
if (pvPropertyData != NULL) { CopyMemory(pvPropertyData, pProperty->pPropertyData, ulDataLength); } *pulBytesReturned = ulDataLength;
return S_OK;
case KSPROPERTY_TYPE_SET: if (!(pProperty->ulSupported & KSPROPERTY_SUPPORT_SET)) { Trace(1, "Error: SynthSink does not support Set for the requested property.\n"); return DMUS_E_SET_UNSUPPORTED; }
if (pProperty->ulFlags & GENPROP_F_FNHANDLER) { GENPROPHANDLER pfn = pProperty->pfnHandler; return (this->*pfn)(pPropertyIn->Id, TRUE, pvPropertyData, &ulDataLength); }
if (ulDataLength > pProperty->cbPropertyData) { ulDataLength = pProperty->cbPropertyData; }
CopyMemory(pProperty->pPropertyData, pvPropertyData, ulDataLength);
return S_OK;
case KSPROPERTY_TYPE_BASICSUPPORT: if (pProperty == NULL) { Trace(1, "Error: Synth does not provide support for requested property type.\n"); return DMUS_E_UNKNOWN_PROPERTY; }
// XXX Find out what convention is for this!!
//
if (ulDataLength < sizeof(DWORD)) { Trace(1, "Error: Data size for property is too small.\n"); return E_INVALIDARG; }
*(LPDWORD)pvPropertyData = pProperty->ulSupported; *pulBytesReturned = sizeof(DWORD);
return S_OK; }
Trace(1, "Error: KSProperty Flags must contain one of: %s\n" "\tKSPROPERTY_TYPE_SET, KSPROPERTY_TYPE_GET, or KSPROPERTY_TYPE_BASICSUPPORT\n"); return E_INVALIDARG; }
STDMETHODIMP CUserModeSynth::KsMethod( PKSMETHOD pMethod, ULONG ulMethodLength, LPVOID pvMethodData, ULONG ulDataLength, PULONG pulBytesReturned) { V_INAME(DirectMusicSynth::IKsContol::KsMethod); V_BUFPTR_WRITE(pMethod, ulMethodLength); V_BUFPTR_WRITE_OPT(pvMethodData, ulDataLength); V_PTR_WRITE(pulBytesReturned, ULONG);
return DMUS_E_UNKNOWN_PROPERTY; } STDMETHODIMP CUserModeSynth::KsEvent( PKSEVENT pEvent, ULONG ulEventLength, LPVOID pvEventData, ULONG ulDataLength, PULONG pulBytesReturned) { V_INAME(DirectMusicSynthPort::IKsContol::KsEvent); V_BUFPTR_WRITE(pEvent, ulEventLength); V_BUFPTR_WRITE_OPT(pvEventData, ulDataLength); V_PTR_WRITE(pulBytesReturned, ULONG);
return DMUS_E_UNKNOWN_PROPERTY; }
/////////////////////////////////////////////////////////////////////
// Implementation of IDirectMusicSynth8
STDMETHODIMP CUserModeSynth::PlayVoice(REFERENCE_TIME rt, DWORD dwVoiceId, DWORD dwChannelGroup, DWORD dwChannel, DWORD dwDLId, PREL prPitch, VREL vrVolume, SAMPLE_TIME stVoiceStart, SAMPLE_TIME stLoopStart, SAMPLE_TIME stLoopEnd ) { HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->PlayVoice(m_pSynthSink8, rt, dwVoiceId, dwChannelGroup, dwChannel, dwDLId, vrVolume, prPitch, stVoiceStart, stLoopStart, stLoopEnd); } else { Trace(1, "Error: Failed wave playback, synth is not properly configured.\n"); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::StopVoice(REFERENCE_TIME rt, DWORD dwVoiceId ) { HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->StopVoice(m_pSynthSink8, rt, dwVoiceId); } else { Trace(1, "Error: Failed stop of wave playback, synth is not properly configured.\n"); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::GetVoiceState(DWORD dwVoice[], DWORD cbVoice, DMUS_VOICE_STATE VoiceState[] ) { V_INAME(IDirectMusicSynth::GetVoiceState); V_PTR_READ(dwVoice, sizeof(DWORD)*cbVoice);
HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) {
hr = m_pSynth->GetVoiceState(dwVoice, cbVoice, VoiceState);
} ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::Refresh(DWORD dwDownloadID, DWORD dwFlags ) { HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->Refresh(dwDownloadID, dwFlags); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::AssignChannelToBuses(DWORD dwChannelGroup, DWORD dwChannel, LPDWORD pdwBuses, DWORD cBuses ) { HRESULT hr = DMUS_E_SYNTHNOTCONFIGURED;
::EnterCriticalSection(&m_CriticalSection); if (m_pSynth) { hr = m_pSynth->AssignChannelToBuses(dwChannelGroup, dwChannel, pdwBuses, cBuses); } else { Trace(1, "Error: Failed synth channel assignment, synth is not properly configured.\n"); } ::LeaveCriticalSection(&m_CriticalSection);
return hr; }
/////////////////////////////////////////////////////////////////////
// Implementation of IDirectSoundSource
STDMETHODIMP CUserModeSynth::SetSink(IDirectSoundConnect* pSinkConnect) { V_INAME(IDirectSoundSink::SetSink); V_INTERFACE_OPT(pSinkConnect);
HRESULT hr = S_OK;
LPVOID ptr = NULL; V_BUFPTR_WRITE_OPT(ptr, 0);
::EnterCriticalSection(&m_CriticalSection);
//>>>>>>>> RELEASE THE DSLINK IF PRESENT !!!!
// FIXME: The calls into the SynthSink8 may require the DSound DLL Mutex. If the Sink
// is making a a call to READ then we end up in a deadlock. We need to be sure that the
// Synth isn't playing when we do this.
if (m_pSynthSink8) { // FIXME: whoever called us->SetSink() should previously have called
// pOldSink->RemoveSource(us) - it shouldn't be our responsibility to
// do this call (??):
// m_pSynthSink8->RemoveSource(this);
m_pSynthSink8->Release(); m_pSynthSink8 = NULL; }
if (pSinkConnect) { // Obtain the IDirectSoundSynthSink interface on the sink
hr = pSinkConnect->QueryInterface(IID_IDirectSoundSynthSink, (void**)&m_pSynthSink8);
if (SUCCEEDED(hr)) { //
// Get the sink's format and validate it
//
WAVEFORMATEX wfx; DWORD dwSize = sizeof wfx; hr = m_pSynthSink8->GetFormat(&wfx, dwSize, NULL); if (SUCCEEDED(hr) && wfx.wBitsPerSample != 16 ) { Trace(1, "Error; Synth can not write to any format other than 16 bit PCM.\n"); hr = DMUS_E_WAVEFORMATNOTSUPPORTED; }
if (SUCCEEDED(hr)) { // Flag the buffer format to be non-interleaved
m_dwChannels = 1; // This synth with a sink is concidered a mono source.
m_dwBufferFlags = BUFFERFLAG_MULTIBUFFER;
if (m_pSynth) { m_pSynth->SetStereoMode(m_dwBufferFlags);
// reset sample rate if it has changed
if (wfx.nSamplesPerSec != (WORD)m_dwSampleRate) { m_pSynth->SetSampleRate(wfx.nSamplesPerSec); }
// disable DX7 Reverb
m_pSynth->SetReverbActive(FALSE); } } } }
::LeaveCriticalSection(&m_CriticalSection);
return hr; }
STDMETHODIMP CUserModeSynth::Seek(ULONGLONG sp) { m_ullPosition = sp/2; // Convert from bytes to samples
return S_OK; }
STDMETHODIMP CUserModeSynth::Read(LPVOID *ppvBuffer, LPDWORD pdwIDs, LPDWORD pdwFuncIDs, LPLONG plPitchBends, DWORD dwBufferCount, PULONGLONG pullLength ) { V_INAME(IDirectMusicSynth::Read); V_PTR_READ(ppvBuffer, sizeof(LPVOID)*dwBufferCount); V_PTR_READ(pdwIDs, sizeof(LPDWORD)*dwBufferCount);
for ( DWORD i = 0; i < dwBufferCount; i++ ) { V_BUFPTR_WRITE(ppvBuffer[i], (DWORD)*pullLength); if ( ppvBuffer[i] == NULL ) { Trace(1, "Error: Read called with NULL buffer.\n"); return E_INVALIDARG; } }
if ( *pullLength > 0x00000000FFFFFFFF ) // can't read more than a DWORD's worth of data
{ Trace(1, "Error: Read called with invalid buffer length.\n"); return E_INVALIDARG; }
if ( dwBufferCount == 0 ) // don't read no buffers
{ Trace(4, "Warning: Read called with 0 buffers.\n"); return E_INVALIDARG; } if (!m_pSynthSink8) { Trace(1, "Error: Synth is not configured, can not play.\n"); return DMUS_E_SYNTHNOTCONFIGURED; } if (!m_fActive) { Trace(3, "Warning: Synth is not active, can not play.\n"); return DMUS_E_SYNTHINACTIVE; }
::EnterCriticalSection(&m_CriticalSection);
if (m_pSynth) { // Mix
DWORD dwLength = (DWORD)(*pullLength)/2; // Convert from bytes to number of samples. Synth assumes 16 bit
m_pSynth->Mix((short**)ppvBuffer, pdwIDs, pdwFuncIDs, plPitchBends, dwBufferCount, m_dwBufferFlags, dwLength, m_ullPosition);
// Increment current sample position in the audio stream
m_ullPosition += dwLength; }
::LeaveCriticalSection(&m_CriticalSection);
return S_OK; }
STDMETHODIMP CUserModeSynth::GetSize(PULONGLONG pcb) { return E_NOTIMPL; }
|