//----------------------------------------------------------------------------- // File: EchoTool.cpp // // Desc: Implements an object based on IDirectMusicTool // that provides echoing effects. // // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #include #include "EchoTool.h" //----------------------------------------------------------------------------- // Name: CEchoTool::CEchoTool() // Desc: //----------------------------------------------------------------------------- CEchoTool::CEchoTool() { m_cRef = 1; // Set to 1 so one call to Release() will free this m_dwEchoNum = 3; // Default to 3 echoes per note m_mtDelay = DMUS_PPQ / 2; // Default to 8th note echoes InitializeCriticalSection(&m_CrSec); } //----------------------------------------------------------------------------- // Name: CEchoTool::~CEchoTool() // Desc: //----------------------------------------------------------------------------- CEchoTool::~CEchoTool() { DeleteCriticalSection(&m_CrSec); } //----------------------------------------------------------------------------- // Name: CEchoTool::QueryInterface() // Desc: //----------------------------------------------------------------------------- STDMETHODIMP CEchoTool::QueryInterface(const IID &iid, void **ppv) { if (iid == IID_IUnknown || iid == IID_IDirectMusicTool) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(this)->AddRef(); return S_OK; } //----------------------------------------------------------------------------- // Name: CEchoTool::AddRef() // Desc: //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEchoTool::AddRef() { return InterlockedIncrement(&m_cRef); } //----------------------------------------------------------------------------- // Name: CEchoTool::Release() // Desc: //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEchoTool::Release() { if( 0 == InterlockedDecrement(&m_cRef) ) { delete this; return 0; } return m_cRef; } //----------------------------------------------------------------------------- // Name: CEchoTool::Init() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::Init( IDirectMusicGraph* pGraph ) { // This tool has no need to do any type of initialization. return E_NOTIMPL; } //----------------------------------------------------------------------------- // Name: CEchoTool::GetMsgDeliveryType() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::GetMsgDeliveryType( DWORD* pdwDeliveryType ) { // This tool wants messages immediately. // This is the default, so returning E_NOTIMPL // would work. The other method is to specifically // set *pdwDeliveryType to the delivery type, DMUS_PMSGF_TOOL_IMMEDIATE, // DMUS_PMSGF_TOOL_QUEUE, or DMUS_PMSGF_TOOL_ATTIME. *pdwDeliveryType = DMUS_PMSGF_TOOL_IMMEDIATE; return S_OK; } //----------------------------------------------------------------------------- // Name: CEchoTool::GetMediaTypeArraySize() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::GetMediaTypeArraySize( DWORD* pdwNumElements ) { // This tool only wants note messages, patch messages, sysex, and MIDI messages, so set // *pdwNumElements to 4. *pdwNumElements = 4; return S_OK; } //----------------------------------------------------------------------------- // Name: CEchoTool::GetMediaTypes() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::GetMediaTypes( DWORD** padwMediaTypes, DWORD dwNumElements ) { // Fill in the array padwMediaTypes with the type of // messages this tool wants to process. In this case, // dwNumElements will be 3, since that is what this // tool returns from GetMediaTypeArraySize(). if( dwNumElements == 4 ) { // Set the elements in the array to DMUS_PMSGT_NOTE, // DMUS_PMSGT_MIDI, and DMUS_PMSGT_PATCH (*padwMediaTypes)[0] = DMUS_PMSGT_NOTE; (*padwMediaTypes)[1] = DMUS_PMSGT_MIDI; (*padwMediaTypes)[2] = DMUS_PMSGT_PATCH; (*padwMediaTypes)[3] = DMUS_PMSGT_SYSEX; return S_OK; } else { // This should never happen return E_FAIL; } } //----------------------------------------------------------------------------- // Name: CEchoTool::ProcessPMsg() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::ProcessPMsg( IDirectMusicPerformance* pPerf, DMUS_PMSG* pPMsg ) { DWORD dwCount; DWORD dwEchoNum; MUSIC_TIME mtDelay; // SetEchoNum() and SetDelay() use these member variables, // so use a critical section to make them thread-safe. EnterCriticalSection(&m_CrSec); dwEchoNum = m_dwEchoNum; mtDelay = m_mtDelay; LeaveCriticalSection(&m_CrSec); // Returning S_FREE frees the message. If StampPMsg() // fails, there is no destination for this message so // free it. if(( NULL == pPMsg->pGraph ) || FAILED(pPMsg->pGraph->StampPMsg(pPMsg))) { return DMUS_S_FREE; } // The Tool is set up to only receive messages of types // DMUS_PMSGT_NOTE, DMUS_PMSGT_MIDI, DMUS_PMSGT_SYSEX, or DMUS_PMSGT_PATCH // We use the DX8 ClonePMsg method to make a copy of the pmsg and // send it to a pchannel in the next pchannel group. // If it's a note, we also doctor the velocity. IDirectMusicPerformance8 *pPerf8; if (SUCCEEDED(pPerf->QueryInterface(IID_IDirectMusicPerformance8,(void **)&pPerf8))) { for( dwCount = 1; dwCount <= dwEchoNum; dwCount++ ) { DMUS_PMSG *pClone; if( SUCCEEDED( pPerf8->ClonePMsg( pPMsg,&pClone))) { // Add to the time of the echoed note pClone->mtTime += (dwCount * mtDelay); if (pPMsg->dwType == DMUS_PMSGT_NOTE ) { DMUS_NOTE_PMSG *pNote = (DMUS_NOTE_PMSG*)pPMsg; DMUS_NOTE_PMSG *pCloneNote = (DMUS_NOTE_PMSG*)pClone; // Reduce the volume of the echoed note // percentage of reduction in velocity increases with each echo pCloneNote->bVelocity = (BYTE) (pNote->bVelocity - ((pNote->bVelocity * (dwCount * 15))/100)); } // Set the note so only MUSIC_TIME is valid. // REFERENCE_TIME will be recomputed inside // SendPMsg() pClone->dwFlags = DMUS_PMSGF_MUSICTIME; pClone->dwPChannel = pPMsg->dwPChannel + (16*dwCount); // Queue the echoed PMsg pPerf->SendPMsg(pClone ); } } pPerf8->Release(); } // Return DMUS_S_REQUEUE so the original message is requeued return DMUS_S_REQUEUE; } //----------------------------------------------------------------------------- // Name: CEchoTool::Flush() // Desc: //----------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CEchoTool::Flush( IDirectMusicPerformance* pPerf, DMUS_PMSG* pDMUS_PMSG, REFERENCE_TIME rt) { // This tool does not need to flush. return E_NOTIMPL; } //----------------------------------------------------------------------------- // Name: CEchoTool::SetEchoNum() // Desc: //----------------------------------------------------------------------------- void CEchoTool::SetEchoNum( DWORD dwEchoNum ) { // ProcessPMsg() uses m_dwEchoNum, so use a critical // section to make it thread-safe. if( dwEchoNum <= MAX_ECHOES ) { EnterCriticalSection(&m_CrSec); m_dwEchoNum = dwEchoNum; LeaveCriticalSection(&m_CrSec); } } //----------------------------------------------------------------------------- // Name: CEchoTool::SetDelay() // Desc: //----------------------------------------------------------------------------- void CEchoTool::SetDelay( MUSIC_TIME mtDelay ) { // ProcessPMsg() uses m_mtDelay, so use a critical // section to make it thread-safe. EnterCriticalSection(&m_CrSec); m_mtDelay = mtDelay; LeaveCriticalSection(&m_CrSec); }