Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
294 lines
8.8 KiB
C++
294 lines
8.8 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: EchoTool.cpp
|
|
//
|
|
// Desc: Implements an object based on IDirectMusicTool
|
|
// that provides echoing effects.
|
|
//
|
|
// Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
#include <dmusici.h>
|
|
#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<IDirectMusicTool*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(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);
|
|
}
|