本篇是创建游戏内核(19)的续篇,有关DirectAudio和DirectShow的基础知识请参阅用DirectX
Audio和DirectShow播放声音和音乐。
没有了音乐,游戏将变得毫无乐趣,可以通过使用类MUSIC_CHANNEL来播放.MID和DirectMusic本地歌曲(.SGT)。
下载源码和工程
来看看类MUSIC_CHANNEL的定义:
//======================================================================================
// This class encapsulate midi file playing.
//======================================================================================
typedef class MUSIC_CHANNEL
{
friend class SOUND;
public:
MUSIC_CHANNEL();
~MUSIC_CHANNEL();
IDirectMusicSegment8* get_dm_segment();
BOOL create(SOUND_PTR sound);
BOOL load(const char* filename);
BOOL free();
BOOL set_dls(DLS* dls);
BOOL play(long volume_percent = 100, long loop_time = 1);
BOOL stop();
long get_volume();
BOOL set_volume(long percent = 100);
BOOL set_tempo(long percent);
BOOL is_playing();
protected:
SOUND_PTR _sound;
IDirectMusicSegment8* _dm_segment;
long _volume;
} *MUSIC_CHANNEL_PTR;
以及类MUSIC_CHANNEL的实现:
//------------------------------------------------------------------------------
// Set volume for the default audio path of DirectMusic perforfamce.
//------------------------------------------------------------------------------
BOOL set_audio_path_volume(IDirectMusicPerformance8* dm_perf, long percent)
{
IDirectMusicAudioPath8* audio_path;
long volume;
if(dm_perf == NULL)
return FALSE;
// retrieves the default audiopath
if(FAILED(dm_perf->GetDefaultAudioPath(&audio_path)))
return FALSE;
// calculate a usable volume level
if(percent == 0)
volume = -9600;
else
volume = (long) (-32.0 * (100.0 - (float)(percent % 101)));
// set the audio volume on the audiopath, the volume can be faded in or out.
if(FAILED(audio_path->SetVolume(volume, 0)))
{
audio_path->Release();
return FALSE;
}
audio_path->Release();
return TRUE;
}
//------------------------------------------------------------------------------
// Set tempo for DirectMusic performance.
//------------------------------------------------------------------------------
BOOL set_performance_tempo(IDirectMusicPerformance8* dm_perf, long percent)
{
if(dm_perf == NULL)
return FALSE;
// calculate tempo setting based on percentage
float tempo = (float) percent / 100.0f;
// set master performance tempo
if(FAILED(dm_perf->SetGlobalParam(GUID_PerfMasterTempo, (void*) &tempo, sizeof(float))))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// 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(_sound && _dm_segment)
{
// unloads instrument data from a performance
if(_sound->get_dm_performance())
{
if(FAILED(_dm_segment->Unload(_sound->get_dm_performance())))
return FALSE;
}
// releases the loader's reference to an object
if(_sound->get_dm_loader())
{
if(FAILED(_sound->get_dm_loader()->ReleaseObjectByUnknown(_dm_segment)))
return FALSE;
}
}
release_com(_dm_segment);
return TRUE;
}
//------------------------------------------------------------------------------
// Stop playing music segment.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::stop()
{
// return if not setup correctly
if(_sound == NULL || _sound->get_dm_performance() == NULL || _dm_segment == NULL)
return FALSE;
// stop palyback
if(FAILED(_sound->get_dm_performance()->Stop(_dm_segment, NULL, 0, 0)))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Return direct music segment.
//------------------------------------------------------------------------------
IDirectMusicSegment8* MUSIC_CHANNEL::get_dm_segment()
{
return _dm_segment;
}
//------------------------------------------------------------------------------
// Attach SOUND object to music channel.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::create(SOUND_PTR sound)
{
free();
// make sure all objects there
if((_sound = sound) == NULL)
return FALSE;
if(_sound->get_dm_performance() == NULL || _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(_sound == NULL)
return FALSE;
if(_sound->get_dm_performance() == NULL || _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(_sound->get_dm_loader()->GetObject(&obj_desc, IID_IDirectMusicSegment8, (LPVOID*) &_dm_segment)))
return FALSE;
// setup MIDI playing
if(strstr(filename, ".mid"))
{
if(FAILED(_dm_segment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
// download band data to a performance
if(FAILED(_dm_segment->Download(_sound->get_dm_performance())))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Download band data from DLS.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_dls(DLS* dls)
{
if(dls == NULL || dls->get_dm_colletion() == NULL)
return FALSE;
if(_sound == NULL || _sound->get_dm_performance() == NULL)
return FALSE;
if(_dm_segment == NULL)
return FALSE;
// sets data on a track inside this segment
if(FAILED(_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(_dm_segment->Unload(_sound->get_dm_performance())))
return FALSE;
// downloads band data to a performance
if(FAILED(_dm_segment->Download(_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(_sound == NULL || _sound->get_dm_performance() == NULL || _dm_segment == NULL)
return FALSE;
// set the number of loops
if(loop_time == 0)
_dm_segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
else
_dm_segment->SetRepeats(loop_time - 1);
// set the playback volume
set_volume(volume_percent);
// play on default audio path
if(FAILED(_sound->get_dm_performance()->PlaySegmentEx(_dm_segment, NULL, NULL, 0, 0, NULL, NULL, NULL)))
return FALSE;
return TRUE;
}
//------------------------------------------------------------------------------
// Get volume of music channel.
//------------------------------------------------------------------------------
long MUSIC_CHANNEL::get_volume()
{
return _volume;
}
//------------------------------------------------------------------------------
// Set volume for music chanel.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_volume(long percent)
{
if(_sound == NULL)
return FALSE;
if(! set_audio_path_volume(_sound->get_dm_performance(), percent))
return FALSE;
_volume = percent % 101;
return TRUE;
}
//------------------------------------------------------------------------------
// Set tempo for DirectMusic performance.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::set_tempo(long percent)
{
if(_sound == NULL)
return FALSE;
return set_performance_tempo(_sound->get_dm_performance(), percent);
}
//------------------------------------------------------------------------------
// Ascertains whether segment is playing.
//------------------------------------------------------------------------------
BOOL MUSIC_CHANNEL::is_playing()
{
// return if not setup correctly
if(_sound == NULL || _sound->get_dm_performance() == NULL || _dm_segment == NULL)
return FALSE;
// ascertains whether a specified segment or segment state is currently being heard from the speakers
if(_sound->get_dm_performance()->IsPlaying(_dm_segment, NULL) == S_OK)
return TRUE;
return FALSE;
}
MUSIC_CHANNEL类同其他类的区别是: MUSIC_CHANNEL类只需要被初始化一次,初始化它使用的函数是MUSIC_CHANNEL::create。
MUSIC_CHANNEL::free函数将歌曲从内存中释放掉,以便给要加载的其他歌曲留出空间。加载歌曲时,要给MUSIC_CHANNEL::load函数提供一个音乐文件名,而且音乐还必须是.MID或DirectMusic本地歌曲(*.SGT)文件。Midi文件必须以.MID结尾,否则MUSIC_CHANNEL函数就不会采用适当的回放格式配置DirectMusic。
一旦加载了歌曲,就可以使用MUSIC_CHANNEL::play函数播放。
测试代码:
/*****************************************************************************
PURPOSE:
Test for class MUSIC_CHANNEL.
*****************************************************************************/
#include "Core_Global.h"
class APP : public APPLICATION
{
public:
BOOL init()
{
// Initialize DierctSound and DirectMusic.
_sound.init(get_hwnd());
// create music channel
_music_channel.create(&_sound);
// Retrieve DirestMusic segment from DirectMusic loader and download band data to performance.
if(! _music_channel.load("song.mid"))
return FALSE;
_music_channel.set_tempo(200);
_music_channel.play(100, 0);
return TRUE;
}
BOOL frame()
{
return TRUE;
}
BOOL shutdown()
{
return TRUE;
}
private:
SOUND _sound;
MUSIC_CHANNEL _music_channel;
};
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
APP app;
return app.run();
}