Windows2003-3790/multimedia/directx/dmusic/dmsynth/umsynth.cpp
2020-09-30 16:53:55 +02:00

1781 lines
52 KiB
C++

//
// 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;
}