/*
游戏引擎内核.Sound类
基于DX9.0封装的C++Sound管理类
http://blog.csdn.net/chinayaosir qq:44633197
*/
/**************************************************
Core_Sound.h
GameCore Component
**************************************************/
#ifndef _CORE_SOUND_H_
#define _CORE_SOUND_H_
// Macro to release a COM object
#define ReleaseCOM(x) if(x) { x->Release(); x = NULL; }
// Macros to help create patches numbers
#define PATCH(m,l,p) ((m << 16) | (l << 8) | p)
#define PATCHMSB(x) ((x >> 16) & 255)
#define PATCHLSB(x) ((x >> 8) & 255)
#define PATCHNUM(x) (x & 255)
// These are the fixed sizes for sound channel buffers
const long g_SoundBufferSize = 65536;
const long g_SoundBufferChunk = g_SoundBufferSize / 4;
// Forward class declarations
class cSound;
class cSoundData;
class cSoundChannel;
class cMusicChannel;
class cDLS;
class cMP3;
class cSound
{
protected:
// Sound system related
HWND m_hWnd;
long m_Volume;
HANDLE m_Events[33];
cSoundChannel *m_EventChannel[32];
HANDLE m_hThread;
DWORD m_ThreadID;
BOOL m_ThreadActive;
static DWORD HandleNotifications(LPVOID lpvoid);
// Sound related
IDirectSound8 *m_pDS;
IDirectSoundBuffer *m_pDSBPrimary;
long m_CooperativeLevel;
long m_Frequency;
short m_Channels;
short m_BitsPerSample;
// Music related - MIDI
IDirectMusicPerformance8 *m_pDMPerformance;
IDirectMusicLoader8 *m_pDMLoader;
public:
cSound();
~cSound();
// Assign and release events
BOOL AssignEvent(cSoundChannel *Channel, short *EventNum, HANDLE *EventHandle);
BOOL ReleaseEvent(cSoundChannel *Channel, short *EventNum);
// Functions to retrieve COM interfaces
IDirectSound8 *GetDirectSoundCOM();
IDirectSoundBuffer *GetPrimaryBufferCOM();
IDirectMusicPerformance8 *GetPerformanceCOM();
IDirectMusicLoader8 *GetLoaderCOM();
// Init and shutdown functions
BOOL Init(HWND hWnd, long Frequency = 22050, short Channels = 1, short BitsPerSample = 16, long CooperativeLevel = DSSCL_PRIORITY);
BOOL Shutdown();
// Volume get/set
long GetVolume();
BOOL SetVolume(long Percent);
// Restore system to known state
BOOL Restore();
};
class cSoundData
{
friend class cSoundChannel;
protected:
long m_Frequency;
short m_Channels;
short m_BitsPerSample;
FILE *m_fp;
char *m_Ptr;
char *m_Buf;
long m_Size;
long m_Left;
long m_StartPos;
long m_Pos;
public:
cSoundData();
~cSoundData();
char *GetPtr();
long GetSize();
BOOL Create();
BOOL Create(long Size);
BOOL Free();
BOOL SetFormat(long Frequency, short Channels, short BitsPerSample);
BOOL SetSource(FILE *fp, long Pos = -1, long Size = -1);
BOOL SetSource(void *Ptr, long Pos = -1, long Size = -1);
BOOL LoadWAV(char *Filename, FILE *fp = NULL);
BOOL LoadWAVHeader(char *Filename, FILE *fp = NULL);
BOOL Copy(cSoundData *Source);
};
class cSoundChannel
{
friend class cSound;
protected:
cSound *m_Sound;
IDirectSoundBuffer8 *m_pDSBuffer;
IDirectSoundNotify8 *m_pDSNotify;
short m_Event;
long m_Volume;
signed long m_Pan;
BOOL m_Playing;
long m_Loop;
long m_Frequency;
short m_BitsPerSample;
short m_Channels;
cSoundData m_Desc;
short m_LoadSection;
short m_StopSection;
short m_NextNotify;
BOOL BufferData();
BOOL Update();
public:
cSoundChannel();
~cSoundChannel();
IDirectSoundBuffer8 *GetSoundBufferCOM();
IDirectSoundNotify8 *GetNotifyCOM();
BOOL Create(cSound *Sound, long Frequency = 22050, short Channels = 1, short BitsPerSample = 16);
BOOL Create(cSound *Sound, cSoundData *SoundDesc);
BOOL Free();
BOOL Play(cSoundData *Desc, long VolumePercent = 100, long Loop = 1);
BOOL Stop();
long GetVolume();
BOOL SetVolume(long Percent);
signed long GetPan();
BOOL SetPan(signed long Level);
long GetFrequency();
BOOL SetFrequency(long Level);
BOOL IsPlaying();
};
class cMusicChannel
{
friend class cSound;
protected:
cSound *m_Sound;
IDirectMusicSegment8 *m_pDMSegment;
long m_Volume;
public:
cMusicChannel();
~cMusicChannel();
IDirectMusicSegment8 *GetSegmentCOM();
BOOL Create(cSound *Sound);
BOOL Load(char *Filename);
BOOL Free();
BOOL SetDLS(cDLS *DLS);
BOOL Play(long VolumePercent = 100, long Loop = 1);
BOOL Stop();
long GetVolume();
BOOL SetVolume(long Percent = 100);
BOOL SetTempo(long Percent = 100);
BOOL IsPlaying();
};
class cDLS
{
protected:
cSound *m_Sound;
IDirectMusicCollection *m_pDMCollection;
public:
cDLS();
~cDLS();
IDirectMusicCollection8 *GetCollectionCOM();
BOOL Create(cSound *Sound);
BOOL Load(char *Filename = NULL);
BOOL Free();
long GetNumPatches();
long GetPatch(long Index);
BOOL Exists(long Patch);
};
class cMP3
{
private:
IGraphBuilder *m_pDSGraphBuilder;
IMediaControl *m_pDSMediaControl;
IMediaEvent *m_pDSMediaEvent;
public:
cMP3();
~cMP3();
// Initialize and shutdown DirectShow
BOOL Init();
BOOL Shutdown();
// Render a file for use
BOOL Render(char *Filename);
// Playback controls
BOOL Play();
BOOL Stop();
BOOL Pause();
// Playback status (return TRUE = still playing)
BOOL Playing();
};
#pragma pack(1)
typedef struct sWaveHeader
{
char RiffSig[4];
long WaveformChunkSize;
char WaveSig[4];
char FormatSig[4];
long FormatChunkSize;
short FormatTag;
short Channels;
long SampleRate;
long BytesPerSec;
short BlockAlign;
short BitsPerSample;
char DataSig[4];
long DataSize;
} sWaveHeader;
#pragma pack()
#endif
/*******************************************************************/
/**************************************************
WinMain.cpp
GameCore Component
**************************************************/
#include "Core_Global.h"
//
//
// cSound Functions
//
//
cSound::cSound()
{
short i;
// Initialize COM
CoInitialize(NULL);
m_hWnd = NULL;
m_Volume = 0;
m_hThread = NULL;
m_ThreadID = NULL;
m_ThreadActive = FALSE;
m_pDMPerformance = NULL;
m_pDMLoader = NULL;
m_pDS = NULL;
m_pDSBPrimary = NULL;
for(i=0;i<32;i++)
m_EventChannel[i] = NULL;
for(i=0;i<33;i++)
m_Events[i] = NULL;
}
cSound::~cSound()
{
Shutdown();
// Uninitialize COM
CoUninitialize();
}
BOOL cSound::Init(HWND hWnd, long Frequency, short Channels, short BitsPerSample, long CooperativeLevel)
{
CHAR strPath[MAX_PATH];
WCHAR wstrSearchPath[MAX_PATH];
DSBUFFERDESC dsbd;
WAVEFORMATEX wfex;
long VolumeLevel;
short i;
// Shutdown system in case of prior install
Shutdown();
// Save parent window handle
if((m_hWnd = hWnd) == NULL)
return FALSE;
///
// Initialize DirectSound
///
// Save settings of sound setup
if(CooperativeLevel == DSSCL_NORMAL)
CooperativeLevel = DSSCL_PRIORITY;
m_CooperativeLevel = CooperativeLevel;
m_Frequency = Frequency;
m_Channels = Channels;
m_BitsPerSample = BitsPerSample;
// create an IDirectSound8 object
if(FAILED(DirectSoundCreate8(NULL, &m_pDS, NULL)))
return FALSE;
// Set cooperative mode
if(FAILED(m_pDS->SetCooperativeLevel(m_hWnd, m_CooperativeLevel)))
return FALSE;
// Get primary buffer control
ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;
if(FAILED(m_pDS->CreateSoundBuffer(&dsbd, &m_pDSBPrimary, NULL)))
return FALSE;
// Set the primary buffer format
ZeroMemory(&wfex, sizeof(WAVEFORMATEX));
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = (WORD)m_Channels;
wfex.nSamplesPerSec = m_Frequency;
wfex.wBitsPerSample = (WORD)m_BitsPerSample;
wfex.nBlockAlign = wfex.wBitsPerSample / 8 * wfex.nChannels;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
if(FAILED(m_pDSBPrimary->SetFormat(&wfex)))
return FALSE;
// Create the events, plus
// an extra one for thread termination
for(i=0;i<33;i++) {
if((m_Events[i] = CreateEvent(NULL,FALSE,FALSE,NULL)) == NULL)
return FALSE;
}
// Create a thread for handling notifications
if((m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HandleNotifications, (LPVOID)this, 0, &m_ThreadID)) == NULL)
return FALSE;
// Start main buffer playing
if(FAILED(m_pDSBPrimary->Play(0, 0, DSBPLAY_LOOPING)))
return FALSE;
///
// Initialize DirectMusic
///
// Create the DirectMusic loader object
CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC,
IID_IDirectMusicLoader8, (void**)&m_pDMLoader);
// Create the DirectMusic performance object
CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,
IID_IDirectMusicPerformance8, (void**)&m_pDMPerformance);
// Initialize the performance with the standard audio path.
// This initializes both DirectMusic and DirectSound and
// sets up the synthesizer.
m_pDMPerformance->InitAudio(NULL, NULL, m_hWnd,
DMUS_APATH_SHARED_STEREOPLUSREVERB, 128,
DMUS_AUDIOF_ALL, NULL);
// set the performance global volume to +10 decibels
VolumeLevel = 1000;
if(FAILED(m_pDMPerformance->SetGlobalParam(GUID_PerfMasterVolume, &VolumeLevel, sizeof(long))))
return FALSE;
// Tell DirectMusic where the default search path is
GetCurrentDirectory(MAX_PATH, strPath);
MultiByteToWideChar(CP_ACP, 0, strPath, -1, wstrSearchPath, MAX_PATH);
m_pDMLoader->SetSearchDirectory(GUID_DirectMusicAllTypes, wstrSearchPath, FALSE);
// Set default volume to full
SetVolume(100);
return TRUE;
}
BOOL cSound::Shutdown()
{
short i;
// Stop the music, and close down
if(m_pDMPerformance != NULL) {
m_pDMPerformance->Stop(NULL, NULL, 0, 0);
m_pDMPerformance->CloseDown();
}
// Release the DirectMusic objects
ReleaseCOM(m_pDMPerformance);
ReleaseCOM(m_pDMLoader);
// Go through all used sound channels and free them
for(i=0;i<32;i++) {
if(m_EventChannel[i] != NULL) {
m_EventChannel[i]->Free();
m_EventChannel[i] = NULL;
}
// Clear the event status
if(m_Events[i] != NULL)
ResetEvent(m_Events[i]);
}
// Stop the primary channel from playing
if(m_pDSBPrimary != NULL)
m_pDSBPrimary->Stop();
// Release the DirectSound objects
ReleaseCOM(m_pDSBPrimary);
ReleaseCOM(m_pDS);
// Force a closure of the thread by triggering the last event
// and waiting for it to terminate
if(m_hThread != NULL) {
if(m_Events[32] != NULL) {
while(m_ThreadActive == TRUE)
SetEvent(m_Events[32]);
} else {
// getting here means no event assigned on thread
// and need to terminate it - not desirable
TerminateThread(m_hThread, 0);
}
}
// Close all event handles
for(i=0;i<33;i++) {
if(m_Events[i] != NULL) {
CloseHandle(m_Events[i]);
m_Events[i] = NULL;
}
}
// Free the thread handle
if(m_hThread != NULL) {
CloseHandle(m_hThread);
m_hThread = NULL;
}
m_ThreadID = NULL;
return TRUE;
}
IDirectSound8 *cSound::GetDirectSoundCOM()
{
return m_pDS;
}
IDirectSoundBuffer *cSound::GetPrimaryBufferCOM()
{
return m_pDSBPrimary;
}
IDirectMusicPerformance8 *cSound::GetPerformanceCOM()
{
return m_pDMPerformance;
}
IDirectMusicLoader8 *cSound::GetLoaderCOM()
{
return m_pDMLoader;
}
BOOL cSound::AssignEvent(cSoundChannel *Channel, short *EventNum, HANDLE *EventHandle)
{
short i;
for(i=0;i<32;i++) {
if(m_Events[i] != NULL && m_EventChannel[i] == NULL) {
ResetEvent(m_Events[i]);
m_EventChannel[i] = Channel;
*EventNum = i;
*EventHandle = m_Events[i];
return TRUE;
}
}
return FALSE;
}
BOOL cSound::ReleaseEvent(cSoundChannel *Channel, short *EventNum)
{
if((unsigned short)*EventNum < 33 && m_EventChannel[*EventNum] == Channel) {
ResetEvent(m_Events[*EventNum]);
m_EventChannel[*EventNum] = NULL;
*EventNum = -1;
return TRUE;
}
return FALSE;
}
long cSound::GetVolume()
{
return m_Volume;
}
BOOL cSound::SetVolume(long Percent)
{
long Volume;
// Set the sound main volume
if(m_pDSBPrimary == NULL)
return FALSE;
// calculate a usable volume level
if(!Percent)
Volume = DSBVOLUME_MIN;
else
Volume = -20 * (100 - (Percent % 101));
if(FAILED(m_pDSBPrimary->SetVolume(Volume)))
return FALSE;
m_Volume = Percent % 101;
return TRUE;
}
DWORD cSound::HandleNotifications(LPVOID lpvoid)
{
DWORD dwResult, Channel;
cSound *SoundPtr;
BOOL Complete;
MSG Msg;
SoundPtr = (cSound*)lpvoid;
SoundPtr->m_ThreadActive = TRUE;
Complete = FALSE;
while(Complete == FALSE) {
// Wait for a message
dwResult = MsgWaitForMultipleObjects(33, SoundPtr->m_Events,
FALSE, INFINITE,
QS_ALLEVENTS);
// Get channel # to update
Channel = dwResult - WAIT_OBJECT_0;
// Check for channel update
if(Channel >=0 && Channel < 32) {
if(SoundPtr->m_EventChannel[Channel] != NULL)
SoundPtr->m_EventChannel[Channel]->Update();
} else
// Check for thread closure
if(Channel == 32) {
Complete = TRUE;
} else
// Check for waiting messages
if(Channel > 32) {
while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
if(Msg.message == WM_QUIT) {
Complete = TRUE;
break;
}
}
}
}
SoundPtr->m_ThreadActive = FALSE;
return 0L;
}
BOOL cSound::Restore()
{
short i;
// Handle primary
if(m_pDSBPrimary != NULL)
m_pDSBPrimary->Restore();
// Handle all used sound channels
for(i=0;i<32;i++) {
if(m_EventChannel[i] != NULL)
m_EventChannel[i]->m_pDSBuffer->Restore();
}
return TRUE;
}
//
//
// cSoundData Functions
//
//
cSoundData::cSoundData()
{
m_Frequency = 22050;
m_Channels = 1;
m_BitsPerSample = 16;
m_fp = NULL;
m_Ptr = m_Buf = NULL;
m_StartPos = m_Pos = 0;
m_Size = m_Left = 0;
}
cSoundData::~cSoundData()
{
Free();
}
BOOL cSoundData::Create()
{
return Create(m_Size);
}
BOOL cSoundData::Create(long Size)
{
// Free prior allocated data
Free();
// Check for valid size
if((m_Size = Size) == 0)
return FALSE;
// Create a new buffer
m_Buf = new char[m_Size];
if(m_Buf == NULL)
return FALSE;
// Point to new buffer
m_Ptr = m_Buf;
m_fp = NULL;
// Clear out new buffer
ZeroMemory(m_Buf, m_Size);
return TRUE;
}
BOOL cSoundData::Free()
{
if(m_Buf != NULL) {
delete[] m_Buf;
m_Buf = NULL;
}
m_Ptr = NULL;
m_Size = 0;
return TRUE;
}
char *cSoundData::GetPtr()
{
return m_Buf;
}
long cSoundData::GetSize()
{
return m_Size;
}
BOOL cSoundData::SetFormat(long Frequency, short Channels, short BitsPerSample)
{
m_Frequency = Frequency;
m_Channels = Channels;
m_BitsPerSample = BitsPerSample;
return TRUE;
}
BOOL cSoundData::SetSource(FILE *fp, long Pos, long Size)
{
m_fp = fp;
m_Ptr = NULL;
if(Pos != -1)
m_StartPos = m_Pos = Pos;
if(Size != -1)
m_Size = m_Left = Size;
return TRUE;
}
BOOL cSoundData::SetSource(void *Ptr, long Pos, long Size)
{
m_fp = NULL;
m_Ptr = (char*)Ptr;
if(Pos != -1)
m_StartPos = m_Pos = Pos;
if(Size != -1)
m_Size = m_Left = Size;
return TRUE;
}
BOOL cSoundData::LoadWAV(char *Filename, FILE *fp)
{
if(LoadWAVHeader(Filename, fp) == FALSE)
return FALSE;
if(Create() == FALSE)
return FALSE;
// open file, seek to position and read in data
if(Filename != NULL) {
if((fp=fopen(Filename, "rb"))==NULL)
return FALSE;
}
fseek(fp, m_StartPos, SEEK_SET);
fread(m_Buf, 1, m_Size, fp);
m_StartPos = m_Pos = 0;
// close up file
if(Filename != NULL)
fclose(fp);
return TRUE;
}
BOOL cSoundData::LoadWAVHeader(char *Filename, FILE *fp)
{
sWaveHeader Hdr;
BOOL ReturnVal;
long Pos;
if(Filename == NULL && fp == NULL)
return FALSE;
if(Filename != NULL) {
if((fp=fopen(Filename, "rb"))==NULL)
return FALSE;
}
// Save position in file
Pos = ftell(fp);
// Read in header and parse
ReturnVal = FALSE;
fread(&Hdr, 1, sizeof(sWaveHeader), fp);
if(!memcmp(Hdr.RiffSig, "RIFF", 4) && !memcmp(Hdr.WaveSig, "WAVE", 4) && !memcmp(Hdr.FormatSig, "fmt ", 4) && !memcmp(Hdr.DataSig, "data", 4)) {
m_Frequency = Hdr.SampleRate;
m_Channels = Hdr.Channels;
m_BitsPerSample = Hdr.BitsPerSample;
m_Size = m_Left = Hdr.DataSize;
m_StartPos = m_Pos = ftell(fp);
ReturnVal = TRUE;
}
// Close if we opened file
// otherwise return to original position
if(Filename != NULL)
fclose(fp);
else
fseek(fp, Pos, SEEK_SET);
return ReturnVal;
}
BOOL cSoundData::Copy(cSoundData *Source)
{
if(Source == NULL)
return FALSE;
m_Frequency = Source->m_Frequency;
m_Channels = Source->m_Channels;
m_BitsPerSample = Source->m_BitsPerSample;
m_fp = Source->m_fp;
m_Ptr = Source->m_Ptr;
m_Size = Source->m_Size;
m_Left = Source->m_Left;
m_Pos = Source->m_Pos;
m_StartPos = Source->m_StartPos;
return TRUE;
}
//
//
// cSoundChannel Functions
//
//
cSoundChannel::cSoundChannel()
{
m_Sound = NULL;
m_pDSBuffer = NULL;
m_pDSNotify = NULL;
m_Event = -1;
m_Volume = 0;
m_Pan = 0;
m_Frequency = 0;
m_Playing = FALSE;
}
cSoundChannel::~cSoundChannel()
{
Free();
}
IDirectSoundBuffer8 *cSoundChannel::GetSoundBufferCOM()
{
return m_pDSBuffer;
}
IDirectSoundNotify8 *cSoundChannel::GetNotifyCOM()
{
return m_pDSNotify;
}
BOOL cSoundChannel::Create(cSound *Sound, long Frequency, short Channels, short BitsPerSample)
{
DSBUFFERDESC dsbd;
WAVEFORMATEX wfex;
HANDLE hEvent;
DSBPOSITIONNOTIFY dspn[4];
IDirectSoundBuffer *pDSBuffer;
// Free a prior channel
Free();
if((m_Sound = Sound) == NULL)
return FALSE;
if(m_Sound->GetDirectSoundCOM() == NULL)
return FALSE;
// Save playback format
m_Frequency = Frequency;
m_BitsPerSample = BitsPerSample;
m_Channels = Channels;
// Create a new sound buffer for this channel
// Using specified format
ZeroMemory(&wfex, sizeof(WAVEFORMATEX));
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = (WORD)m_Channels;
wfex.nSamplesPerSec = m_Frequency;
wfex.wBitsPerSample = (WORD)m_BitsPerSample;
wfex.nBlockAlign = wfex.wBitsPerSample / 8 * wfex.nChannels;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = g_SoundBufferSize;
dsbd.lpwfxFormat = &wfex;
if(FAILED(m_Sound->GetDirectSoundCOM()->CreateSoundBuffer(&dsbd, &pDSBuffer, NULL)))
return FALSE;
// query for newer interface
if(FAILED(pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&m_pDSBuffer))) {
pDSBuffer->Release();
return FALSE;
}
// Release old object - we have the newer one now
pDSBuffer->Release();
// Create the notification interface
if(FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&m_pDSNotify)))
//if(FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&m_pDSNotify)))
return FALSE;
// Get an event for this
if(m_Sound->AssignEvent(this, &m_Event, &hEvent) == FALSE)
return FALSE;
// Setup the 4 notification positions
dspn[0].dwOffset = g_SoundBufferChunk - 1;
dspn[0].hEventNotify = hEvent;
dspn[1].dwOffset = g_SoundBufferChunk * 2 - 1;
dspn[1].hEventNotify = hEvent;
dspn[2].dwOffset = g_SoundBufferChunk * 3 - 1;
dspn[2].hEventNotify = hEvent;
dspn[3].dwOffset = g_SoundBufferSize - 1;
dspn[3].hEventNotify = hEvent;
if(FAILED(m_pDSNotify->SetNotificationPositions(4, dspn)))
return FALSE;
// set the pan and default volume
SetVolume(100);
SetPan(0);
return TRUE;
}
BOOL cSoundChannel::Create(cSound *Sound, cSoundData *SoundDesc)
{
return Create(Sound, SoundDesc->m_Frequency, SoundDesc->m_Channels, SoundDesc->m_BitsPerSample);
}
BOOL cSoundChannel::Free()
{
// Stop any playback
Stop();
// Release the notification
ReleaseCOM(m_pDSNotify);
// Release the buffer
ReleaseCOM(m_pDSBuffer);
// Release event from parent cSound class
m_Sound->ReleaseEvent(this, &m_Event);
// Set to no parent sound
m_Sound = NULL;
return TRUE;
}
BOOL cSoundChannel::Play(cSoundData *Desc, long VolumePercent, long Loop)
{
if(Desc == NULL)
return FALSE;
if(m_pDSBuffer == NULL)
return FALSE;
if(m_pDSNotify == NULL)
return FALSE;
// Stop any playback
Stop();
// Restore a lost buffer just in case
m_pDSBuffer->Restore();
// Setup playing information
m_Desc.Copy(Desc);
// Set looping data
m_Loop = Loop;
// Calculate stop section position
if(!m_Loop)
m_StopSection = -1;
else
m_StopSection = (short)(((m_Desc.m_Size * m_Loop) % g_SoundBufferSize) / g_SoundBufferChunk);
// Buffer in data
m_LoadSection = 0;
BufferData();
BufferData();
BufferData();
BufferData();
// Set the volume
SetVolume(VolumePercent);
// Set position and begin play
m_NextNotify = 0;
if(FAILED(m_pDSBuffer->SetCurrentPosition(0)))
return FALSE;
if(FAILED(m_pDSBuffer->Play(0,0,DSBPLAY_LOOPING)))
return FALSE;
// Flag as playing
m_Playing = TRUE;
return TRUE;
}
BOOL cSoundChannel::Stop()
{
if(m_pDSBuffer)
m_pDSBuffer->Stop();
m_Playing = FALSE;
return TRUE;
}
long cSoundChannel::GetVolume()
{
return m_Volume;
}
BOOL cSoundChannel::SetVolume(long Percent)
{
long Volume;
if(m_pDSBuffer == NULL)
return FALSE;
// calculate a usable volume level
if(!Percent)
Volume = DSBVOLUME_MIN;
else
Volume = -20 * (100 - (Percent % 101));
if(FAILED(m_pDSBuffer->SetVolume(Volume)))
return FALSE;
m_Volume = Percent % 101;
return TRUE;
}
signed long cSoundChannel::GetPan()
{
return m_Pan;
}
BOOL cSoundChannel::SetPan(signed long Level)
{
signed long Pan;
if(m_pDSBuffer == NULL)
return FALSE;
// calculate a usable setting
if(Level < 0)
Pan = DSBPAN_LEFT / 100 * ((-Level) % 101);
else
Pan = DSBPAN_RIGHT / 100 * (Level % 101);
if(FAILED(m_pDSBuffer->SetPan(Pan)))
return FALSE;
m_Pan = Level % 101;
return TRUE;
}
long cSoundChannel::GetFrequency()
{
return m_Frequency;
}
BOOL cSoundChannel::SetFrequency(long Level)
{
if(m_pDSBuffer == NULL)
return FALSE;
if(FAILED(m_pDSBuffer->SetFrequency(Level)))
return FALSE;
m_Frequency = Level;
return TRUE;
}
BOOL cSoundChannel::IsPlaying()
{
if(m_Sound == NULL || m_pDSBuffer == NULL || m_pDSNotify == NULL)
return FALSE;
return m_Playing;
}
BOOL cSoundChannel::BufferData()
{
long Pos, Size;
long ToLoad, LoadPos;
char *Ptr;
if(m_pDSBuffer == NULL)
return FALSE;
// Setup position to load in
Pos = (m_LoadSection % 4) * g_SoundBufferChunk;
if(FAILED(m_pDSBuffer->Lock(Pos, g_SoundBufferChunk, (void**)&Ptr, (DWORD*)&Size, NULL, NULL, 0)))
return FALSE;
// Clear out buffer if nothing left to load
if(!m_Desc.m_Left)
ZeroMemory(Ptr, Size);
else {
// Load in the data - take looping into account
ToLoad = Size;
LoadPos = 0;
for(;;) {
if(m_Desc.m_Left > ToLoad) {
if(m_Desc.m_fp != NULL) {
fseek(m_Desc.m_fp, m_Desc.m_Pos, SEEK_SET);
fread(&Ptr[LoadPos], 1, ToLoad, m_Desc.m_fp);
} else
memcpy(&Ptr[LoadPos], &m_Desc.m_Ptr[m_Desc.m_Pos], ToLoad);
m_Desc.m_Left -= ToLoad;
m_Desc.m_Pos += ToLoad;
break;
} else {
if(m_Desc.m_fp != NULL) {
fseek(m_Desc.m_fp, m_Desc.m_Pos, SEEK_SET);
fread(&Ptr[LoadPos], 1, m_Desc.m_Left, m_Desc.m_fp);
} else
memcpy(&Ptr[LoadPos], &m_Desc.m_Ptr[m_Desc.m_Pos], m_Desc.m_Left);
ToLoad -= m_Desc.m_Left;
LoadPos += m_Desc.m_Left;
// Check if we need to stop loop
if(m_Loop >= 1) {
m_Loop--;
if(!m_Loop) {
// Clear out remaining buffer space
if(ToLoad)
ZeroMemory(&Ptr[LoadPos], ToLoad);
m_Desc.m_Left = 0L;
break;
}
}
m_Desc.m_Pos = m_Desc.m_StartPos;
m_Desc.m_Left = m_Desc.m_Size;
// See if we need to stop loading data
if(!ToLoad)
break;
}
}
}
// Unlock the buffer
m_pDSBuffer->Unlock(Ptr, Size, NULL, 0);
// Mark next section to load
if((m_LoadSection+=1) > 3)
m_LoadSection = 0;
return TRUE;
}
BOOL cSoundChannel::Update()
{
// Check for end of sound
if(m_NextNotify == m_StopSection && !m_Desc.m_Left) {
Stop();
} else {
// Buffer in more data
BufferData();
if((m_NextNotify+=1) > 3)
m_NextNotify = 0;
}
return TRUE;
}
//
//
// cMusicChannel Functions
//
//
cMusicChannel::cMusicChannel()
{
m_Sound = NULL;
m_pDMSegment = NULL;
m_Volume = 0;
}
cMusicChannel::~cMusicChannel()
{
Free();
}
IDirectMusicSegment8 *cMusicChannel::GetSegmentCOM()
{
return m_pDMSegment;
}
BOOL cMusicChannel::Create(cSound *Sound)
{
Free();
// Make sure all objects there
if((m_Sound = Sound) == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_Sound->GetLoaderCOM() == NULL)
return FALSE;
return TRUE;
}
BOOL cMusicChannel::Load(char *Filename)
{
DMUS_OBJECTDESC dmod;
Free();
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_Sound->GetLoaderCOM() == NULL)
return FALSE;
// Get the object
ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));
dmod.dwSize = sizeof(DMUS_OBJECTDESC);
dmod.guidClass = CLSID_DirectMusicSegment;
dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
mbstowcs(dmod.wszFileName, Filename, MAX_PATH);
if(FAILED(m_Sound->GetLoaderCOM()->GetObject(&dmod, IID_IDirectMusicSegment8, (LPVOID*)&m_pDMSegment)))
return FALSE;
// Setup MIDI playing
if(strstr(Filename, ".mid") != NULL) {
if(FAILED(m_pDMSegment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
// Download the band
if(FAILED(m_pDMSegment->Download(m_Sound->GetPerformanceCOM())))
return FALSE;
return TRUE;
}
BOOL cMusicChannel::Free()
{
Stop();
if(m_Sound != NULL) {
// unload instrument data
if(m_pDMSegment != NULL) {
if(m_Sound->GetPerformanceCOM() != NULL) {
if(FAILED(m_pDMSegment->Unload(m_Sound->GetPerformanceCOM())))
return FALSE;
}
// free loader data
if(m_Sound->GetLoaderCOM() != NULL) {
if(FAILED(m_Sound->GetLoaderCOM()->ReleaseObjectByUnknown(m_pDMSegment)))
return FALSE;
}
}
}
// release the segment
ReleaseCOM(m_pDMSegment);
return TRUE;
}
BOOL cMusicChannel::SetDLS(cDLS *DLS)
{
if(DLS == NULL)
return FALSE;
if(DLS->GetCollectionCOM() == NULL)
return FALSE;
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_pDMSegment == NULL)
return FALSE;
// Connect to the collection
if(FAILED(m_pDMSegment->SetParam(GUID_ConnectToDLSCollection, 0xFFFFFFFF, 0, 0, (void*)DLS->GetCollectionCOM())))
return FALSE;
// unload and then re-download new instruments
if(FAILED(m_pDMSegment->Unload(m_Sound->GetPerformanceCOM())))
return FALSE;
if(FAILED(m_pDMSegment->Download(m_Sound->GetPerformanceCOM())))
return FALSE;
return TRUE;
}
BOOL cMusicChannel::Play(long VolumePercent, long Loop)
{
Stop();
// Return if not setup correctly
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_pDMSegment == NULL)
return FALSE;
// Set the number of loops
if(!Loop)
m_pDMSegment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
else
m_pDMSegment->SetRepeats(Loop-1);
// Set the playback volume
SetVolume(VolumePercent);
// Play on default audio path
if(FAILED(m_Sound->GetPerformanceCOM()->PlaySegmentEx(
m_pDMSegment, NULL, NULL,
0, 0, NULL, NULL, NULL)))
return FALSE;
return TRUE;
}
BOOL cMusicChannel::Stop()
{
// Return if not setup correctly
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_pDMSegment == NULL)
return FALSE;
// Stop playback
if(FAILED(m_Sound->GetPerformanceCOM()->Stop(m_pDMSegment, NULL, 0, 0)))
return FALSE;
return TRUE;
}
long cMusicChannel::GetVolume()
{
return m_Volume;
}
BOOL cMusicChannel::SetVolume(long Percent)
{
IDirectMusicAudioPath8 *pDMAudioPath;
long Volume;
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(FAILED(m_Sound->GetPerformanceCOM()->GetDefaultAudioPath(&pDMAudioPath)))
return FALSE;
// calculate a usable volume level
if(!Percent)
Volume = -9600;
else
Volume = (long)(-32.0 * (100.0 - (float)(Percent % 101)));
if(FAILED(pDMAudioPath->SetVolume(Volume,0))) {
pDMAudioPath->Release();
return FALSE;
}
pDMAudioPath->Release();
m_Volume = Percent % 101;
return TRUE;
}
BOOL cMusicChannel::SetTempo(long Percent)
{
float Tempo;
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
// calculate tempo setting based on percentage
Tempo = (float)Percent / 100.0f;
// set master performance tempo
if(FAILED(m_Sound->GetPerformanceCOM()->SetGlobalParam(GUID_PerfMasterTempo, (void*)&Tempo, sizeof(float))))
return FALSE;
return TRUE;
}
BOOL cMusicChannel::IsPlaying()
{
// Return if not setup correctly
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM() == NULL)
return FALSE;
if(m_pDMSegment == NULL)
return FALSE;
if(m_Sound->GetPerformanceCOM()->IsPlaying(m_pDMSegment, NULL) == S_OK)
return TRUE;
return FALSE;
}
cDLS::cDLS()
{
m_Sound = NULL;
m_pDMCollection = NULL;
}
cDLS::~cDLS()
{
Free();
}
BOOL cDLS::Create(cSound *Sound)
{
Free();
if((m_Sound = Sound) == NULL)
return FALSE;
if(m_Sound->GetLoaderCOM() == NULL)
return FALSE;
return TRUE;
}
BOOL cDLS::Load(char *Filename)
{
DMUS_OBJECTDESC dmod;
Free();
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetLoaderCOM() == NULL)
return FALSE;
ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));
dmod.dwSize = sizeof(DMUS_OBJECTDESC);
dmod.guidClass = CLSID_DirectMusicCollection;
if(Filename == NULL) {
// Get the default collection
dmod.guidObject = GUID_DefaultGMCollection;
dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_OBJECT;
} else {
// Get the collection object
dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
mbstowcs(dmod.wszFileName, Filename, MAX_PATH);
}
if(FAILED(m_Sound->GetLoaderCOM()->GetObject(&dmod, IID_IDirectMusicCollection8, (LPVOID*)&m_pDMCollection)))
return FALSE;
return TRUE;
}
BOOL cDLS::Free()
{
if(m_Sound == NULL)
return FALSE;
if(m_Sound->GetLoaderCOM() == NULL)
return FALSE;
if(m_pDMCollection != NULL) {
if(FAILED(m_Sound->GetLoaderCOM()->ReleaseObjectByUnknown(m_pDMCollection)))
return FALSE;
}
ReleaseCOM(m_pDMCollection);
return TRUE;
}
IDirectMusicCollection8 *cDLS::GetCollectionCOM()
{
return m_pDMCollection;
}
long cDLS::GetNumPatches()
{
HRESULT hr;
DWORD dwPatch;
long i;
hr = S_OK;
for(i=0; hr == S_OK; i++) {
hr = m_pDMCollection->EnumInstrument(i, &dwPatch, NULL, 0);
if(hr != S_OK)
break;
}
return i;
}
long cDLS::GetPatch(long Index)
{
DWORD dwPatch;
if(m_pDMCollection == NULL)
return -1;
if(FAILED(m_pDMCollection->EnumInstrument(Index, &dwPatch, NULL, 0)))
return -1;
return (long)dwPatch;
}
BOOL cDLS::Exists(long Patch)
{
IDirectMusicInstrument8 *pDMInstrument;
if(m_pDMCollection == NULL)
return FALSE;
if(FAILED(m_pDMCollection->GetInstrument(Patch, &pDMInstrument)))
return FALSE;
pDMInstrument->Release();
return TRUE;
}
IGraphBuilder *m_pDSGraphBuilder;
IMediaControl *m_pDSMediaControl;
IMediaEvent *m_pDSMediaEvent;
cMP3::cMP3()
{
m_pDSGraphBuilder = NULL;
m_pDSMediaControl = NULL;
m_pDSMediaEvent = NULL;
}
cMP3::~cMP3()
{
Shutdown();
}
BOOL cMP3::Init()
{
Shutdown(); // Shutdown prior initialized system
// Initialize COM
CoInitialize(NULL);
// Create graph builder object and media controls
if(FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, /
CLSCTX_INPROC_SERVER, /
IID_IGraphBuilder, /
(void**)&m_pDSGraphBuilder)))
return FALSE;
m_pDSGraphBuilder->QueryInterface(IID_IMediaControl, /
(void**)&m_pDSMediaControl);
m_pDSGraphBuilder->QueryInterface(IID_IMediaEvent, /
(void**)&m_pDSMediaEvent);
return TRUE;
}
BOOL cMP3::Shutdown()
{
ReleaseCOM(m_pDSMediaEvent);
ReleaseCOM(m_pDSMediaControl);
ReleaseCOM(m_pDSGraphBuilder);
CoUninitialize();
return TRUE;
}
BOOL cMP3::Render(char *Filename)
{
// Error checking
if(m_pDSGraphBuilder == NULL || m_pDSMediaControl == NULL)
return FALSE;
// Stop the current song
m_pDSMediaControl->Stop();
// Convert filename to wide-character string
WCHAR wFilename[MAX_PATH];
mbstowcs(wFilename, Filename, MAX_PATH);
// Render the file
if(FAILED(m_pDSGraphBuilder->RenderFile(wFilename, NULL)))
return FALSE;
// Return success
return TRUE;
}
BOOL cMP3::Play()
{
// Error checking
if(m_pDSMediaControl == NULL)
return FALSE;
// Try to play media
if(FAILED(m_pDSMediaControl->Run()))
return FALSE;
return TRUE;
}
BOOL cMP3::Stop()
{
// Error checking
if(m_pDSMediaControl == NULL)
return FALSE;
// Try to stop media
if(FAILED(m_pDSMediaControl->Stop()))
return FALSE;
return TRUE;
}
BOOL cMP3::Pause()
{
// Error checking
if(m_pDSMediaControl == NULL)
return FALSE;
// Try to pause media
if(FAILED(m_pDSMediaControl->Pause()))
return FALSE;
return TRUE;
}
BOOL cMP3::Playing()
{
// Error checking
if(m_pDSMediaEvent == NULL)
return FALSE;
// Get event and handle it
long Event, Param1, Param2;
m_pDSMediaEvent->GetEvent(&Event, &Param1, &Param2, 1);
if(Event == EC_COMPLETE) {
m_pDSMediaEvent->FreeEventParams(Event, Param1, Param2);
return TRUE;
}
m_pDSMediaEvent->FreeEventParams(Event, Param1, Param2);
return FALSE;
}