// // 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 #include #include #include #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 // @@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(this); } else if (iid == IID_IKsControl) { *ppv = static_cast(this); } else if (iid == IID_IDirectMusicSynth8 ) { *ppv = static_cast(this); } else if (iid == IID_IDirectSoundSource) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(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) // 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(this)); } ::LeaveCriticalSection(&m_CriticalSection); return hr; } STDMETHODIMP CUserModeSynth::Open( LPDMUS_PORTPARAMS pPortParams) // 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 and used later to unload the data. LPVOID pvData, // Pointer to continuous memory segment with download data. LPBOOL pbFree) //

indicates whether the synthesizer wishes to keep the memory in

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 . HRESULT ( CALLBACK *lpFreeHandle)(HANDLE, HANDLE), // If the original call to // returned FALSE in

, // 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 // since a wave might be currently in use.

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

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 . // And, this should be after the time returned by the clock in // . LPBYTE pbBuffer, // Memory chunk with all the MIDI events, generated by . 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) // 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 , // used by all devices in current instance of DirectMusic. { V_INAME(IDirectMusicSynth::SetMasterClock); V_INTERFACE(pClock); return S_OK; } STDMETHODIMP CUserModeSynth::GetLatencyClock( IReferenceClock **ppClock) // 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 // command. LONGLONG llPosition) // Position in the audio stream, also in samples. // This should always increment by

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