本篇是创建游戏内核(19)【OO改良版】的续篇,关于该内核的细节说明请参阅创建游戏内核(20)。
接口:
//======================================================================================
// This class encapsulate midi file playing.
//======================================================================================
typedef class MUSIC_CHANNEL
{
friend class SOUND;
public:
MUSIC_CHANNEL();
~MUSIC_CHANNEL();
IDirectMusicSegment8* get_dm_segment();
BOOL attach(SOUND_PTR sound);
BOOL load(const char* filename);
BOOL free();
BOOL set_dls(DLS* dls, const char* filename);
BOOL play(long volume_percent, long loop_time);
BOOL stop();
long get_volume();
BOOL set_volume(long percent);
BOOL set_tempo(long percent);
BOOL is_playing();
protected:
SOUND_PTR m_sound;
IDirectMusicSegment8* m_dm_segment;
long m_volume;
} *MUSIC_CHANNEL_PTR;
实现:
//------------------------------------------------------------------------------
// Constructor, initialize member data.
//------------------------------------------------------------------------------
MUSIC_CHANNEL::MUSIC_CHANNEL()
{
memset(this, 0, sizeof(*this));
}
//------------------------------------------------------------------------------
// Destructor, release resource.
//------------------------------------------------------------------------------
MUSIC_CHANNEL::~MUSIC_CHANNEL()
{
free();
}
//------------------------------------------------------------------------------
// Release DirectMusic segment and loader resource.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::free()
{
stop();
// unload instrument data
if(m_sound && m_dm_segment)
{
// unloads instrument data from a performance
if(m_sound->get_dm_performance())
{
if(FAILED(m_dm_segment->Unload(m_sound->get_dm_performance())))
return FALSE;
}
// releases the loader's reference to an object
if(m_sound->get_dm_loader())
{
if(FAILED(m_sound->get_dm_loader()->ReleaseObjectByUnknown(m_dm_segment)))
return FALSE;
}
}
release_com(m_dm_segment);
return TRUE;
}
//------------------------------------------------------------------------------
// Stop playing music segment.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::stop()
{
// return if not setup correctly
if(m_sound == NULL || m_sound->get_dm_performance() == NULL || m_dm_segment == NULL)
return FALSE;
// stop palyback
if(FAILED(m_sound->get_dm_performance()->Stop(m_dm_segment, NULL, 0, 0)))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Return direct music segment.
//------------------------------------------------------------------------------
IDirectMusicSegment8* MUSIC_CHANNEL::get_dm_segment()
{
return m_dm_segment;
}
//------------------------------------------------------------------------------
// Attach SOUND object to music channel.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::attach(SOUND_PTR sound)
{
free();
// make sure all objects there
if((m_sound = sound) == NULL)
return FALSE;
if(m_sound->get_dm_performance() == NULL || m_sound->get_dm_loader() == NULL)
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Retrieve DirestMusic segment from DirectMusic loader and download band data to
// performance.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::load(const char* filename)
{
free();
if(m_sound == NULL)
return FALSE;
if(m_sound->get_dm_performance() == NULL || m_sound->get_dm_loader() == NULL)
return FALSE;
// setup DMUS_OBJECTDESC structure which used to describe a loadable object
DMUS_OBJECTDESC _obj_desc;
ZeroMemory(&_obj_desc, sizeof(DMUS_OBJECTDESC));
_obj_desc.dwSize = sizeof(DMUS_OBJECTDESC);
_obj_desc.guidClass = CLSID_DirectMusicSegment;
_obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
mbstowcs(_obj_desc.wszFileName, filename, MAX_PATH);
// retrieves an objet from a file or resource and returns the specified interface
if(FAILED(m_sound->get_dm_loader()->GetObject(&_obj_desc, IID_IDirectMusicSegment8, (LPVOID*) &m_dm_segment)))
return FALSE;
// setup MIDI playing
if(strstr(filename, ".mid"))
{
if(FAILED(m_dm_segment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
// download band data to a performance
if(FAILED(m_dm_segment->Download(m_sound->get_dm_performance())))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Download band data from DLS.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_dls(DLS* dls, const char* filename)
{
if(dls == NULL || dls->get_dm_colletion() == NULL)
return FALSE;
if(m_sound == NULL || m_sound->get_dm_performance() == NULL)
return FALSE;
if(m_dm_segment == NULL)
{
DMUS_OBJECTDESC _obj_desc;
ZeroMemory(&_obj_desc, sizeof(DMUS_OBJECTDESC));
_obj_desc.dwSize = sizeof(DMUS_OBJECTDESC);
_obj_desc.guidClass = CLSID_DirectMusicSegment;
_obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
mbstowcs(_obj_desc.wszFileName, filename, MAX_PATH);
// retrieves an objet from a file or resource and returns the specified interface
if(FAILED(m_sound->get_dm_loader()->GetObject(&_obj_desc, IID_IDirectMusicSegment8, (LPVOID*) &m_dm_segment)))
return FALSE;
}
// sets data on a track inside this segment
if(FAILED(m_dm_segment->SetParam(GUID_ConnectToDLSCollection, 0xFFFFFFFF, 0, 0, (void*) dls->get_dm_colletion())))
return FALSE;
// unload and then re-download new instruments
// unloads instrument data from a performance
if(FAILED(m_dm_segment->Unload(m_sound->get_dm_performance())))
return FALSE;
// downloads band data to a performance
if(FAILED(m_dm_segment->Download(m_sound->get_dm_performance())))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Play music segment.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::play(long volume_percent, long loop_time)
{
stop();
// return if no setup coorectly
if(m_sound == NULL || m_sound->get_dm_performance() == NULL || m_dm_segment == NULL)
return FALSE;
// set the number of loops
if(loop_time == 0)
m_dm_segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
else
m_dm_segment->SetRepeats(loop_time - 1);
// set the playback volume
set_volume(volume_percent);
// play on default audio path
if(FAILED(m_sound->get_dm_performance()->PlaySegmentEx(m_dm_segment, NULL, NULL, 0, 0, NULL, NULL, NULL)))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Get volume of music channel.
//------------------------------------------------------------------------------
long MUSIC_CHANNEL::get_volume()
{
return m_volume;
}
//------------------------------------------------------------------------------
// Set volume for music chanel.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_volume(long percent)
{
if(m_sound == NULL)
return FALSE;
if(! set_audio_path_volume(m_sound->get_dm_performance(), percent))
return FALSE;
m_volume = percent % 101;
return TRUE;
}
//------------------------------------------------------------------------------
// Set tempo for DirectMusic performance.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_tempo(long percent)
{
if(m_sound == NULL)
return FALSE;
return set_performance_tempo(m_sound->get_dm_performance(), percent);
}
//------------------------------------------------------------------------------
// Ascertains whether segment is playing.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::is_playing()
{
// return if not setup correctly
if(m_sound == NULL || m_sound->get_dm_performance() == NULL || m_dm_segment == NULL)
return FALSE;
// ascertains whether a specified segment or segment state is currently being heard from the speakers
if(m_sound->get_dm_performance()->IsPlaying(m_dm_segment, NULL) == S_OK)
return TRUE;
return FALSE;
}
测试代码:
/*****************************************************************************
PURPOSE:
Test for class MUSIC_CHANNEL.
*****************************************************************************/
#include "core_common.h"
#include "core_framework.h"
#include "core_sound.h"
class APP : public FRAMEWORK
{
public:
BOOL init()
{
// Initialize DierctSound and DirectMusic.
m_sound.init(g_hwnd, 22050, 1, 16, DSSCL_NORMAL);
// create music channel
m_music_channel.attach(&m_sound);
// Retrieve DirestMusic segment from DirectMusic loader and download band data to performance.
if(! m_music_channel.load("song.mid"))
return FALSE;
m_music_channel.set_tempo(150);
m_music_channel.play(100, 0);
return TRUE;
}
BOOL frame()
{
return TRUE;
}
BOOL shutdown()
{
return TRUE;
}
private:
SOUND m_sound;
MUSIC_CHANNEL m_music_channel;
};
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
APP app;
if(! build_window(inst, "MainClass", "MainWindow", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480))
return -1;
app.run();
return 0;
}