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>
1116 lines
27 KiB
C++
1116 lines
27 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: Music.cpp
|
|
//
|
|
// Desc:
|
|
//
|
|
// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <oleauto.h>
|
|
#include <stdio.h>
|
|
#include "DXUtil.h"
|
|
#include "music.h"
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
BoidMusic::BoidMusic()
|
|
{
|
|
m_dwBeatsSinceLastMotif = 0;
|
|
m_pSegment = NULL;
|
|
m_pPort = NULL;
|
|
m_pDMusic = NULL;
|
|
m_fCollapsed = FALSE;
|
|
m_dwIndex = 0;
|
|
m_pBand = NULL;
|
|
m_pStyle = NULL;
|
|
m_pChordMap = NULL;
|
|
long x;
|
|
for (x = 0; x < 6; x++)
|
|
{
|
|
m_pTemplateSegments[x] = NULL;
|
|
m_pPrimarySegments[x] = NULL;
|
|
m_pMotifSegments[x] = NULL;
|
|
}
|
|
m_pPrimarySegments[1] = NULL;
|
|
m_pTransitionSegment = NULL;
|
|
m_pComposer = NULL;
|
|
m_pLoader = NULL;
|
|
m_pPerformance = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
BoidMusic::~BoidMusic()
|
|
{
|
|
long x;
|
|
if (m_pSegment)
|
|
{
|
|
m_pSegment->Release();
|
|
}
|
|
if (m_pBand)
|
|
{
|
|
if(m_pPerformance)
|
|
{
|
|
m_pBand->Unload(m_pPerformance);
|
|
}
|
|
m_pBand->Release();
|
|
}
|
|
if (m_pGraph)
|
|
{
|
|
m_pGraph->Release();
|
|
}
|
|
if (m_pPort)
|
|
{
|
|
m_pPort->Release();
|
|
}
|
|
if (m_pDMusic)
|
|
{
|
|
m_pDMusic->Release();
|
|
}
|
|
for (x = 0; x< 6; x++)
|
|
{
|
|
if (m_pTemplateSegments[x])
|
|
{
|
|
m_pTemplateSegments[x]->Release();
|
|
}
|
|
if (m_pPrimarySegments[x])
|
|
{
|
|
m_pPrimarySegments[x]->Release();
|
|
}
|
|
if (m_pMotifSegments[x])
|
|
{
|
|
m_pMotifSegments[x]->Release();
|
|
}
|
|
}
|
|
if (m_pStyle)
|
|
{
|
|
m_pStyle->Release();
|
|
}
|
|
if (m_pChordMap)
|
|
{
|
|
m_pChordMap->Release();
|
|
}
|
|
if (m_pTransitionSegment)
|
|
{
|
|
m_pTransitionSegment->Release();
|
|
}
|
|
if (m_pComposer)
|
|
{
|
|
m_pComposer->Release();
|
|
}
|
|
if (m_pLoader)
|
|
{
|
|
m_pLoader->Release();
|
|
}
|
|
if (m_pPerformance)
|
|
{
|
|
m_pPerformance->Release();
|
|
}
|
|
if( m_hNotify )
|
|
{
|
|
CloseHandle( m_hNotify );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::LoadStyle()
|
|
{
|
|
if (m_pStyle) m_pStyle->Release();
|
|
|
|
DMUS_OBJECTDESC ObjectDescript;
|
|
ObjectDescript.guidClass = CLSID_DirectMusicStyle;
|
|
wcscpy(ObjectDescript.wszFileName, L"Boids2.sty");
|
|
ObjectDescript.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME ;
|
|
ObjectDescript.dwSize = sizeof(DMUS_OBJECTDESC);
|
|
if (SUCCEEDED(m_pLoader->GetObject(&ObjectDescript, IID_IDirectMusicStyle, (void**)&m_pStyle)))
|
|
{
|
|
BSTR bstr = SysAllocString(L"Default");
|
|
m_pStyle->GetBand(bstr, &m_pBand) ;
|
|
SysFreeString(bstr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: This loads a precomposed segment.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::LoadSegment()
|
|
{
|
|
if (m_pSegment) m_pSegment->Release();
|
|
|
|
DMUS_OBJECTDESC ObjectDescript;
|
|
ObjectDescript.guidClass = CLSID_DirectMusicSegment;
|
|
wcscpy(ObjectDescript.wszFileName, L"BoidsD.sgt");
|
|
ObjectDescript.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME ;
|
|
ObjectDescript.dwSize = sizeof(DMUS_OBJECTDESC);
|
|
if (SUCCEEDED(m_pLoader->GetObject(&ObjectDescript, IID_IDirectMusicSegment, (void**)&m_pSegment)))
|
|
{
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Load the dls set that the band needs, but just leave it in the cache.
|
|
// Then, it will be referenced and loaded by the bands in the styles and
|
|
// segments.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT BoidMusic::LoadDLS()
|
|
{
|
|
DMUS_OBJECTDESC dmod;
|
|
dmod.dwSize = sizeof(DMUS_OBJECTDESC);
|
|
dmod.guidClass = CLSID_DirectMusicCollection;
|
|
dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME ;
|
|
wcscpy(dmod.wszFileName, L"Boids.dls");
|
|
|
|
IDirectMusicCollection* pCollect = NULL;
|
|
if( FAILED( m_pLoader->GetObject( &dmod, IID_IDirectMusicCollection,
|
|
(VOID**)&pCollect ) ) )
|
|
return E_FAIL;
|
|
|
|
pCollect->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Load the Boids chord map.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::LoadChordMap()
|
|
{
|
|
if (m_pChordMap) m_pChordMap->Release();
|
|
|
|
DMUS_OBJECTDESC ObjectDescript;
|
|
ObjectDescript.guidClass = CLSID_DirectMusicChordMap;
|
|
wcscpy(ObjectDescript.wszFileName, L"Boids.cdm");
|
|
ObjectDescript.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
|
|
ObjectDescript.dwSize = sizeof(DMUS_OBJECTDESC);
|
|
if (SUCCEEDED(m_pLoader->GetObject(&ObjectDescript, IID_IDirectMusicChordMap, (void**)&m_pChordMap)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Load a template and place it in the m_pTemplateSegments[] array.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::LoadTemplate(DWORD dwIndex, WCHAR * pwzName)
|
|
{
|
|
if (m_pTemplateSegments[dwIndex]) m_pTemplateSegments[dwIndex]->Release();
|
|
|
|
HRESULT hr;
|
|
DMUS_OBJECTDESC ObjectDescript;
|
|
|
|
ObjectDescript.guidClass = CLSID_DirectMusicSegment;
|
|
wcscpy(ObjectDescript.wszFileName, pwzName);
|
|
ObjectDescript.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
|
|
ObjectDescript.dwSize = sizeof(DMUS_OBJECTDESC);
|
|
if (SUCCEEDED( hr = m_pLoader->GetObject(&ObjectDescript, IID_IDirectMusicSegment, (void**)&m_pTemplateSegments[dwIndex])))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Use the composer to compose a style segment from a previously loaded
|
|
// template segment, the boids chord map and style. Place the
|
|
// segment in the m_pPrimarySegments[] array.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::ComposeSegment(DWORD dwIndex)
|
|
{
|
|
|
|
if (m_pComposer && m_pStyle && m_pChordMap && m_pTemplateSegments[dwIndex])
|
|
{
|
|
if (m_pPrimarySegments[dwIndex])
|
|
{
|
|
m_pPrimarySegments[dwIndex]->Release();
|
|
m_pPrimarySegments[dwIndex] = NULL;
|
|
}
|
|
m_pComposer->ComposeSegmentFromTemplate(
|
|
m_pStyle,
|
|
m_pTemplateSegments[dwIndex],
|
|
1,
|
|
m_pChordMap,
|
|
&m_pPrimarySegments[dwIndex]);
|
|
if (m_pPrimarySegments[dwIndex])
|
|
{
|
|
m_pPrimarySegments[dwIndex]->SetRepeats(100);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Pull the named motif segment from the boids style.
|
|
// Note that we currently use the OLE BSTR string approach.
|
|
// This will probably change in the next beta.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::GetMotif(DWORD dwIndex, WCHAR * pszName)
|
|
{
|
|
if (m_pStyle)
|
|
{
|
|
if (SUCCEEDED(m_pStyle->GetMotif(pszName, &m_pMotifSegments[dwIndex])))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Check for a notify event. Since we are only waiting for
|
|
// notification of beats, we increment out beat counter. The beat counter
|
|
// is used by the boids app to manage the number of motifs allowed at any
|
|
// point in time. This keeps things from getting too overcrowded.
|
|
// Alternatively, the notifications could be used to synchronize graphic
|
|
// events.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::HandleNotifies()
|
|
{
|
|
DMUS_NOTIFICATION_PMSG* pMsg;
|
|
if( m_hNotify && m_pPerformance)
|
|
{
|
|
WaitForSingleObject(m_hNotify, 2);
|
|
while( S_OK == m_pPerformance->GetNotificationPMsg(&pMsg))
|
|
{
|
|
m_pPerformance->FreePMsg((DMUS_PMSG*)pMsg);
|
|
m_dwBeatsSinceLastMotif++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: This is called by the app, once it gets its main window up.
|
|
// Activate the synth and start the music playing.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::StartMusic()
|
|
{
|
|
if (m_pDMusic)
|
|
{
|
|
m_pDMusic->Activate(TRUE);
|
|
}
|
|
if (m_pPrimarySegments[0])
|
|
{
|
|
m_pPerformance->PlaySegment(
|
|
m_pPrimarySegments[0], 0, 0, NULL);
|
|
m_dwIndex = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: This is called from the graphics loop. As we get closer to the center
|
|
// of the flock of birds, increase the number of echoes for the echo tool,
|
|
// and also increase the duration scalings (makes the note durations
|
|
// longer.)
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::SetDistance(double fDistance)
|
|
{
|
|
double fTemp = fDistance - 10.0;
|
|
fTemp = 70.0 - fTemp;
|
|
if (fTemp < 0.0) fTemp = 0.0;
|
|
m_Tool.m_dwEcho = (DWORD)(fTemp / 15.0);
|
|
m_Tool.m_dwStartRatio = (DWORD)(75 + fTemp);
|
|
m_Tool.m_dwRatio = m_Tool.m_dwStartRatio;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Whenever the flock is about to hit a sphere, this is called. It
|
|
// chooses the next segment to go to and plays the planet motif leading
|
|
// into the next segment. Notice how it takes the start time of the
|
|
// planet motif, adds one, and uses that to start the segment, but on a
|
|
// beat boundary. This tricks the segment to start one beat later than
|
|
// the motif, creating a smooth musical transition.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::Transition()
|
|
{
|
|
if (!m_fCollapsed && (m_dwBeatsSinceLastMotif > 2))
|
|
{
|
|
static DWORD dwChoice[8] = { 0, 2, 1, 3, 0, 4, 1, 5 };
|
|
m_dwIndex++;
|
|
if (m_dwIndex > 8) m_dwIndex = 2;
|
|
if (m_dwIndex > 0)
|
|
{
|
|
if (m_pMotifSegments[0] && m_pPrimarySegments[dwChoice[m_dwIndex-1]])
|
|
{
|
|
IDirectMusicSegmentState *pState = NULL;
|
|
m_pPerformance->PlaySegment(
|
|
m_pMotifSegments[0], (DMUS_SEGF_SECONDARY | DMUS_SEGF_BEAT ), 0, &pState);
|
|
if (pState)
|
|
{
|
|
MUSIC_TIME mtTime;
|
|
pState->GetStartTime(&mtTime);
|
|
pState->Release();
|
|
mtTime++;
|
|
m_pPerformance->PlaySegment(
|
|
m_pPrimarySegments[dwChoice[m_dwIndex-1]], DMUS_SEGF_BEAT , mtTime, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: When the migration force is turned off, the birds start to migrate
|
|
// away. So, this starts the migration theme. Note that it plays once,
|
|
// and then one of the regular segments is queued to pick up after it.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::Migrate()
|
|
{
|
|
if (m_pSegment)
|
|
{
|
|
m_pPerformance->PlaySegment(
|
|
m_pSegment, DMUS_SEGF_BEAT , 0, NULL);
|
|
}
|
|
if (m_pPrimarySegments[2])
|
|
{
|
|
m_pPerformance->PlaySegment(
|
|
m_pPrimarySegments[2], DMUS_SEGF_QUEUE , 0, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: When the 's' key or space bar is hit, the birds collapse together. Set
|
|
// the collapsed flag indicating the new state. Trigger two motifs to
|
|
// play. The first simply plays the collapse motif. The second plays a
|
|
// cycling motif until the user lifts up, calling the Expand() function.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::Collapse()
|
|
{
|
|
m_fCollapsed = TRUE;
|
|
if (m_dwBeatsSinceLastMotif)
|
|
{
|
|
if (m_pMotifSegments[5])
|
|
{
|
|
m_pPerformance->PlaySegment(
|
|
m_pMotifSegments[5], (DMUS_SEGF_SECONDARY | DMUS_SEGF_GRID), 0, NULL);
|
|
}
|
|
if (m_pMotifSegments[3])
|
|
{
|
|
m_pMotifSegments[3]->SetRepeats(20);
|
|
m_pPerformance->PlaySegment(
|
|
m_pMotifSegments[3], (DMUS_SEGF_SECONDARY | DMUS_SEGF_MEASURE), 0, NULL);
|
|
}
|
|
m_dwBeatsSinceLastMotif = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: When the user releases the 's' key or space bar, the birds can move
|
|
// apart again. Notice that the repeating motif is turned off by calling
|
|
// stop on it.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::Expand()
|
|
{
|
|
m_fCollapsed = FALSE;
|
|
if (m_dwBeatsSinceLastMotif)
|
|
{
|
|
if (m_pMotifSegments[4])
|
|
{
|
|
m_pPerformance->PlaySegment(
|
|
m_pMotifSegments[4], (DMUS_SEGF_SECONDARY | DMUS_SEGF_GRID), 0, NULL);
|
|
}
|
|
m_dwBeatsSinceLastMotif = 0;
|
|
}
|
|
if (m_pMotifSegments[3])
|
|
{
|
|
m_pPerformance->Stop(m_pMotifSegments[3], NULL, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc: Close down all the music stuff.
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::EndMusic()
|
|
{
|
|
m_Tool.m_pPerformance = NULL;
|
|
if (m_pPerformance)
|
|
{
|
|
m_pPerformance->Stop( NULL, NULL, 0, 0);
|
|
}
|
|
if (m_pSegment)
|
|
{
|
|
m_pSegment->Release();
|
|
m_pSegment = NULL;
|
|
}
|
|
if (m_pBand)
|
|
{
|
|
if(m_pPerformance)
|
|
{
|
|
m_pBand->Unload(m_pPerformance);
|
|
}
|
|
m_pBand->Release();
|
|
m_pBand = NULL;
|
|
}
|
|
if (m_pPort)
|
|
{
|
|
m_pPort->Release();
|
|
m_pPort = NULL;
|
|
}
|
|
if (m_pDMusic)
|
|
{
|
|
m_pDMusic->Release();
|
|
m_pDMusic = NULL;
|
|
}
|
|
if (m_pGraph)
|
|
{
|
|
m_pGraph->Release();
|
|
m_pGraph = NULL;
|
|
}
|
|
long x;
|
|
for (x = 0; x< 6; x++)
|
|
{
|
|
if (m_pTemplateSegments[x])
|
|
{
|
|
m_pTemplateSegments[x]->Release();
|
|
m_pTemplateSegments[x] = NULL;
|
|
}
|
|
if (m_pPrimarySegments[x])
|
|
{
|
|
m_pPrimarySegments[x]->Release();
|
|
m_pPrimarySegments[x] = NULL;
|
|
}
|
|
if (m_pMotifSegments[x])
|
|
{
|
|
m_pMotifSegments[x]->Release();
|
|
m_pMotifSegments[x] = NULL;
|
|
}
|
|
}
|
|
if (m_pStyle)
|
|
{
|
|
m_pStyle->Release();
|
|
m_pStyle = NULL;
|
|
}
|
|
if (m_pChordMap)
|
|
{
|
|
m_pChordMap->Release();
|
|
m_pChordMap = NULL;
|
|
}
|
|
if (m_pTransitionSegment)
|
|
{
|
|
m_pTransitionSegment->Release();
|
|
m_pTransitionSegment = NULL;
|
|
}
|
|
if (m_pComposer)
|
|
{
|
|
m_pComposer->Release();
|
|
m_pComposer = NULL;
|
|
}
|
|
if (m_pLoader)
|
|
{
|
|
m_pLoader->Release();
|
|
m_pLoader = NULL;
|
|
}
|
|
if (m_pPerformance)
|
|
{
|
|
m_pPerformance->Stop( NULL, NULL, 0, 0);
|
|
m_pPerformance->CloseDown();
|
|
m_pPerformance->Release();
|
|
m_pPerformance = NULL;
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
|
|
static char szDirectMusicMedia[] = "Media";
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
BOOL BoidMusic::GetSearchPath(WCHAR wszPath[MAX_PATH])
|
|
{
|
|
const TCHAR* szDir = DXUtil_GetDXSDKMediaPath();
|
|
mbstowcs(wszPath, szDir, MAX_PATH);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tool implementation follows. Pretty much all the meat is in the
|
|
// ProcessEvent() method.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
CTool::CTool()
|
|
{
|
|
m_dwRatio = 100;
|
|
m_dwStartRatio = 100;
|
|
m_dwEcho = 1;
|
|
m_cRef = 0;
|
|
m_dwDelay = 96;
|
|
m_pPerformance = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
CTool::~CTool()
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CTool::QueryInterface( const IID &iid, void **ppv)
|
|
{
|
|
if (iid == IID_IDirectMusicTool)
|
|
{
|
|
*ppv = static_cast<IDirectMusicTool*>(this);
|
|
} else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(this)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) CTool::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) CTool::Release()
|
|
{
|
|
if (!InterlockedDecrement(&m_cRef))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::ProcessPMsg( IDirectMusicPerformance* pPerf,
|
|
DMUS_PMSG* pMsg )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (pMsg->pGraph)
|
|
{
|
|
if (SUCCEEDED(pMsg->pGraph->StampPMsg(pMsg)))
|
|
{
|
|
hr = DMUS_S_REQUEUE;
|
|
}
|
|
}
|
|
switch( pMsg->dwType )
|
|
{
|
|
case DMUS_PMSGT_NOTE:
|
|
{
|
|
/* If we receive a note, first adjust the duration
|
|
by m_dwRatio.
|
|
*/
|
|
DMUS_NOTE_PMSG *pNote = (DMUS_NOTE_PMSG *) pMsg;
|
|
if (m_dwRatio < 1) m_dwRatio = 1;
|
|
if (m_dwRatio > 400) m_dwRatio = 400;
|
|
pNote->mtDuration = MulDiv(pNote->mtDuration,m_dwRatio,100);
|
|
/* Then, if it is on the PChannel of the melody,
|
|
set it to echo the number of times established
|
|
by m_dwEcho. Note that the echo algorithm is
|
|
quite simple but powerful: copy the event and play
|
|
it on an adjacent PChannel. By playing it on a different
|
|
PChannel, we take care of overlapping notes and
|
|
we can have it panned differently.
|
|
*/
|
|
if (m_dwEcho && (pNote->dwPChannel == 7))
|
|
{
|
|
if (pPerf)
|
|
{
|
|
DWORD dwX;
|
|
if (m_dwEcho > 3) m_dwEcho = 3;
|
|
for (dwX = 0; dwX < m_dwEcho; dwX++)
|
|
{
|
|
DMUS_NOTE_PMSG * pEcho;
|
|
if (SUCCEEDED(pPerf->AllocPMsg( sizeof(DMUS_NOTE_PMSG),
|
|
(DMUS_PMSG**) &pEcho)))
|
|
{
|
|
static DWORD dwChannels[3] = { 6, 15, 0 };
|
|
memcpy( pEcho, pNote, sizeof(DMUS_NOTE_PMSG) );
|
|
pEcho->dwPChannel = dwChannels[dwX];
|
|
pEcho->mtTime = pNote->mtTime + (m_dwDelay * (dwX + 1));
|
|
pEcho->dwFlags = DMUS_PMSGF_MUSICTIME;
|
|
if (pEcho->pGraph) pEcho->pGraph->AddRef();
|
|
if (pEcho->pTool) pEcho->pTool->AddRef();
|
|
pPerf->SendPMsg( (DMUS_PMSG*)pEcho) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case DMUS_PMSGT_CURVE:
|
|
break;
|
|
case DMUS_PMSGT_SYSEX:
|
|
break;
|
|
case DMUS_PMSGT_MIDI:
|
|
/* Also, if we receive program changes, volumes, or pans on the
|
|
PChannel of the melody, set up the echos to use the same patch,
|
|
a diminishing volume, and assign the pans appropriately.
|
|
This way, if a different band with different setting is loaded,
|
|
the echoed parts will all get reset appropriately.
|
|
*/
|
|
{
|
|
DMUS_MIDI_PMSG *pMidi = (DMUS_MIDI_PMSG *) pMsg;
|
|
if (pMidi->dwPChannel == 7)
|
|
{
|
|
DWORD dwX;
|
|
for (dwX = 0;dwX < 3; dwX++)
|
|
{
|
|
DMUS_MIDI_PMSG * pEcho;
|
|
if (SUCCEEDED(m_pPerformance->AllocPMsg( sizeof(DMUS_MIDI_PMSG),
|
|
(DMUS_PMSG**) &pEcho)))
|
|
{
|
|
static DWORD dwChannels[3] = { 6, 15, 0 };
|
|
static BYTE bPans[3] = { 80,0,120 };
|
|
memcpy( pEcho, pMidi, sizeof(DMUS_MIDI_PMSG) );
|
|
pEcho->dwPChannel = dwChannels[dwX];
|
|
pEcho->mtTime = pMsg->mtTime + 20;
|
|
pEcho->dwFlags = DMUS_PMSGF_MUSICTIME;
|
|
if (pMidi->bStatus == 0xB0)
|
|
{
|
|
if (pMidi->bByte1 == 10)
|
|
{
|
|
pEcho->bByte2 = bPans[dwX];
|
|
}
|
|
else if (pMidi->bByte1 == 7)
|
|
{
|
|
pEcho->bByte2 = pMidi->bByte2 - (((BYTE)dwX+1) * 20);
|
|
if (pEcho->bByte2 > 127) pEcho->bByte2 = 10;
|
|
}
|
|
}
|
|
if (pEcho->pGraph) pEcho->pGraph->AddRef();
|
|
if (pEcho->pTool) pEcho->pTool->AddRef();
|
|
m_pPerformance->SendPMsg( (DMUS_PMSG*)pEcho) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case DMUS_PMSGT_PATCH:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::Flush( IDirectMusicPerformance* pPerf,
|
|
DMUS_PMSG* pMsg, REFERENCE_TIME rt )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::Init( IDirectMusicGraph* pGraph )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::GetMsgDeliveryType( DWORD* pdwDeliveryType )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::GetMediaTypeArraySize( DWORD* pdwNumElements )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CTool::GetMediaTypes( DWORD** padwMediaTypes,
|
|
DWORD dwNumElements )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT BoidMusic::LoadMusic( HWND hwnd )
|
|
{
|
|
HRESULT hr;
|
|
|
|
CoInitialize(NULL);
|
|
hr = CoCreateInstance( CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC,
|
|
IID_IDirectMusicLoader, (VOID**)&m_pLoader );
|
|
if( FAILED(hr) )
|
|
{
|
|
OutputDebugString("Couldn't create CLSID_DirectMusicLoader\n");
|
|
return hr;
|
|
}
|
|
|
|
WCHAR dir[MAX_PATH];
|
|
|
|
m_pLoader->EnableCache(GUID_DirectMusicAllTypes, TRUE);
|
|
|
|
hr = E_FAIL;
|
|
if (BoidMusic::GetSearchPath(dir))
|
|
{
|
|
hr = m_pLoader->SetSearchDirectory(GUID_DirectMusicAllTypes, dir, FALSE);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
hr = m_pLoader->SetSearchDirectory(GUID_DirectMusicAllTypes, L".", FALSE);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't set DirectMusic search path");
|
|
return hr;
|
|
}
|
|
|
|
hr = ::CoCreateInstance( CLSID_DirectMusicComposer, NULL, CLSCTX_INPROC,
|
|
IID_IDirectMusicComposer, (VOID**)&m_pComposer);
|
|
if( FAILED(hr) )
|
|
{
|
|
OutputDebugString("Couldn't create CLSID_DirectMusicComposer\n");
|
|
return hr;
|
|
}
|
|
|
|
BOOL bResourcesOk = TRUE;
|
|
|
|
bResourcesOk = bResourcesOk && SUCCEEDED( LoadDLS() );
|
|
bResourcesOk = bResourcesOk && LoadStyle();
|
|
bResourcesOk = bResourcesOk && LoadChordMap();
|
|
bResourcesOk = bResourcesOk && LoadTemplate(0,L"BoidsAA.tpl");
|
|
bResourcesOk = bResourcesOk && LoadTemplate(1,L"BoidB1.tpl");
|
|
bResourcesOk = bResourcesOk && LoadTemplate(2,L"BoidsC1.tpl");
|
|
bResourcesOk = bResourcesOk && LoadTemplate(3,L"BoidsCA.tpl");
|
|
bResourcesOk = bResourcesOk && LoadTemplate(4,L"BoidsCB.tpl");
|
|
|
|
bResourcesOk = bResourcesOk && LoadTemplate(5,L"BoidsCC.tpl");
|
|
|
|
ComposeSegment(0);
|
|
ComposeSegment(1);
|
|
ComposeSegment(2);
|
|
ComposeSegment(3);
|
|
ComposeSegment(4);
|
|
ComposeSegment(5);
|
|
|
|
bResourcesOk = bResourcesOk && LoadSegment();
|
|
bResourcesOk = bResourcesOk && GetMotif(0, L"planet");
|
|
bResourcesOk = bResourcesOk && GetMotif(1, L"D motif");
|
|
bResourcesOk = bResourcesOk && GetMotif(2, L"motif RL med");
|
|
bResourcesOk = bResourcesOk && GetMotif(3, L"Cycle");
|
|
bResourcesOk = bResourcesOk && GetMotif(4, L"Expand");
|
|
bResourcesOk = bResourcesOk && GetMotif(5, L"Collapse");
|
|
|
|
if (!bResourcesOk)
|
|
{
|
|
OutputDebugString("Couldn't load DirectMusic resources\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = CoCreateInstance( CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IDirectMusic, (VOID**)&m_pDMusic);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't create CLSID_DirectMusic\n");
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pDMusic->SetDirectSound(NULL, hwnd);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Could't SetDirectSound on IDirectMusic\n");
|
|
return hr;
|
|
}
|
|
|
|
DMUS_PORTPARAMS dmos;
|
|
DMUS_PORTCAPS dmpc;
|
|
GUID guidSynthGUID;
|
|
|
|
hr = m_pDMusic->GetDefaultPort(&guidSynthGUID);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Could't GetDefaultPort on IDirectMusic\n");
|
|
return hr;
|
|
}
|
|
|
|
ZeroMemory(&dmos, sizeof(dmos));
|
|
dmos.dwSize = sizeof(DMUS_PORTPARAMS);
|
|
dmos.dwChannelGroups = 1;
|
|
dmos.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
|
|
|
|
hr = m_pDMusic->CreatePort( guidSynthGUID, &dmos, &m_pPort, NULL );
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't CreatePort on IDirectMusic\n");
|
|
return hr;
|
|
}
|
|
|
|
ZeroMemory(&dmpc, sizeof(dmpc));
|
|
dmpc.dwSize = sizeof(DMUS_PORTCAPS);
|
|
|
|
hr = m_pPort->GetCaps(&dmpc);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't GetCaps on IDirectMusicPort\n");
|
|
return hr;
|
|
}
|
|
|
|
if ((dmpc.dwClass != DMUS_PC_OUTPUTCLASS) || !(dmpc.dwFlags & DMUS_PC_DLS))
|
|
{
|
|
m_pPort->Release();
|
|
m_pPort = NULL;
|
|
}
|
|
|
|
if (!m_pPort)
|
|
{
|
|
hr = E_FAIL;
|
|
|
|
for (DWORD index = 0; ; index++)
|
|
{
|
|
ZeroMemory(&dmpc, sizeof(dmpc));
|
|
dmpc.dwSize = sizeof(DMUS_PORTCAPS);
|
|
|
|
hr = m_pDMusic->EnumPort(index, &dmpc);
|
|
if(SUCCEEDED(hr) && hr != S_FALSE)
|
|
{
|
|
if ( (dmpc.dwClass == DMUS_PC_OUTPUTCLASS) &&
|
|
(dmpc.dwFlags & DMUS_PC_DLS) )
|
|
{
|
|
CopyMemory(&guidSynthGUID, &dmpc.guidPort, sizeof(GUID));
|
|
|
|
ZeroMemory(&dmos, sizeof(dmos));
|
|
dmos.dwSize = sizeof(DMUS_PORTPARAMS);
|
|
dmos.dwChannelGroups = 1;
|
|
dmos.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
|
|
|
|
hr = m_pDMusic->CreatePort( guidSynthGUID, &dmos,
|
|
&m_pPort, NULL );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't find or CreatePort software synthesizer\n");
|
|
return hr;
|
|
}
|
|
|
|
hr = CoCreateInstance( CLSID_DirectMusicPerformance, NULL, CLSCTX_ALL,
|
|
IID_IDirectMusicPerformance, (VOID**)&m_pPerformance );
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't create CLSID_DirectMusicPerformance\n");
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pPerformance->Init(&m_pDMusic, NULL, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't init CLSID_DirectMusicPerformance\n");
|
|
return hr;
|
|
}
|
|
|
|
m_pPerformance->AddPort(m_pPort );
|
|
m_pPerformance->AssignPChannelBlock(0, m_pPort, 1);
|
|
m_hNotify = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if( m_hNotify )
|
|
{
|
|
GUID guid;
|
|
m_pPerformance->SetNotificationHandle( m_hNotify, 0 );
|
|
guid = GUID_NOTIFICATION_MEASUREANDBEAT;
|
|
m_pPerformance->AddNotificationType( guid );
|
|
}
|
|
m_Tool.m_pPerformance = m_pPerformance;
|
|
if (m_pBand)
|
|
{
|
|
m_pBand->Download(m_pPerformance);
|
|
}
|
|
|
|
hr = ::CoCreateInstance( CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC,
|
|
IID_IDirectMusicGraph, (VOID**)&m_pGraph );
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't create CLSID_DirectMusicGraph\n");
|
|
return hr;
|
|
}
|
|
|
|
m_pPerformance->SetGraph(m_pGraph);
|
|
IDirectMusicTool *pTool;
|
|
hr = m_Tool.QueryInterface(IID_IDirectMusicTool, (void**)&pTool);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString("Couldn't InsertTool on graph\n");
|
|
return hr;
|
|
}
|
|
|
|
m_pGraph->InsertTool( pTool, 0, NULL, 0);
|
|
|
|
return S_OK;;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
void BoidMusic::Activate( BOOL bActive )
|
|
{
|
|
if (m_pDMusic)
|
|
{
|
|
m_pDMusic->Activate(bActive);
|
|
}
|
|
}
|