本篇是
用DirectX Audio和DirectShow播放声音和音乐(5)的续篇。
加载音色库(乐器)
DirectMusic
加载器在使用固有文件或者MIDI文件的时候会自动加载默认的音色库。乐器总是被一组一组地使用,很多组乐器音色的集合被称之为DLS音色库(可下载的音乐)。每组乐器使用三个值编号,它们是:最高有效位(most-significant
byte,MSB),最低有效位(least-significant byte,LSB)和组编号。
通常播放MIDI文件的乐器组是标准化的,也就是说编号为1的乐器总是钢琴,如果想使用新的钢琴作为乐器,可以从DLS集合中加载。DirectMusic包含了标准的乐器集合,通常称之为GM/GS集合(GM
= General MIDI,GS = General Synthesizer),这个集合由日本罗兰(Roland)公司提出,称为MIDI合成器标准。
如果使用新的乐器取代标准MIDI乐器库中的乐器,需要确定该乐器的MSB和LSB为0,否则就需要为乐器库中的每个乐器都尝试新的赋值,以免打乱乐器库乐器排列。如果只想修改音色库中的一对乐器,只需要将它们保存在乐器库中即可。在下次加载DLS的时候,就会自动用新修改的乐器覆盖住内存中的旧乐器,如下图所示:
![](http://www.cppblog.com/images/cppblog_com/lovedday/4157/o_dls_instrument.jpg)
当DLS加载完成的时候,就可以通知 DirectMusic使用音色库对音乐进行播放了。
加载DLS音色库,需要从加载器中获取一个IDirectMusicCollection8对象,然后再次使用IDirectMusicLoader8::GetObject加载音色库,但是这一次指定的是音色库对象和音色库文件名。
以下代码演示了如何加载指定的音色库:
//--------------------------------------------------------------------------------
// Load DirectMusic collection object.
//--------------------------------------------------------------------------------
IDirectMusicCollection8* Load_DLS_Collection(char* filename)
{
DMUS_OBJECTDESC dm_obj_desc;
IDirectMusicCollection8* dm_collection;
// get the object
ZeroMemory(&dm_obj_desc, sizeof(DMUS_OBJECTDESC));
dm_obj_desc.dwSize = sizeof(DMUS_OBJECTDESC);
dm_obj_desc.guidClass = CLSID_DirectMusicCollection;
dm_obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
// Converts a sequence of multibyte characters to a corresponding sequence of wide characters
mbstowcs(dm_obj_desc.wszFileName, filename, MAX_PATH);
// retrieves an object from a file or resource and returns the speficied interface
if(FAILED(g_dm_loader->GetObject(&dm_obj_desc, IID_IDirectMusicCollection8, (LPVOID*)&dm_collection)))
return NULL;
return dm_collection;
}
音色库被加载后,还需要给音乐片段指定音色库,这个工作通过设置指定的音乐片段的音轨参数来完成,设置参数使用函数
IDirectMusicSegment8::SetParam。
The SetParam method sets data on a track inside this
segment.
Syntax
HRESULT SetParam(
REFGUID rguidType,
DWORD dwGroupBits,
DWORD dwIndex,
MUSIC_TIME mtTime,
void* pParam
);
Parameters
rguidType
Reference to (C++) or address of (C) the type of data to set.
See Standard Track Parameters.
dwGroupBits
Group that the desired track is in. Use 0xFFFFFFFF for all
groups. For more information, see Identifying the Track.
dwIndex
Index of the track in the group identified by dwGroupBits
in which to set the data, or DMUS_SEG_ALLTRACKS to set the parameter on all
tracks in the group that contain the parameter.
mtTime
Time at which to set the data.
pParam
Address of a structure containing the data, or NULL if no data
is required for this parameter. The structure must be of the appropriate kind
and size for the data type identified by rguidType.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_SET_UNSUPPORTED |
DMUS_E_TRACK_NOT_FOUND |
E_POINTER |
MIDI的配置
一首歌曲如果已经和音色库一起被完整地加载到了内存中,这首音乐基本上已经可以使用了,唯一存在的问题是因为系统需要进行配置,以便适应一般MIDI文件的配置,所以需要告诉系统加载的文件是否是一个MIDI文件。告诉DirectMusic使用的是MIDI文件,需要再一次调用参数配置函数
IDirectMusicSegment8:: SetParam。
以下代码演示了如何使用设置MIDI配置:
// retrieves an object from a file or resource and returns the speficied interface
if(FAILED(g_dm_loader->GetObject(&dm_obj_desc, IID_IDirectMusicSegment8, (LPVOID*)&g_dm_segment)))
return FALSE;
// setup midi playing
if(strstr(filename, ".mid"))
{
// set data on a track inside the segment
if(FAILED(g_dm_segment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
播放音乐的下一个准备工作就是配置音色库,将音色库装载到演奏器中,通过调用IDirectMusicSegment8:: Download函数来完成操作。
The Download method downloads band data to a performance or
audiopath.
Syntax
HRESULT Download(
IUnknown* pAudioPath
);
Parameters
pAudioPath
Pointer to the IUnknown interface of the
performance or audiopath that receives the data.
Return Values
If the method succeeds, the return value is S_OK or DMUS_S_PARTIALDOWNLOAD.
See Remarks for IDirectMusicBand8::Download.
If it fails, the method may return one of the error values shown in the
following table.
Return code |
DMUS_E_NOT_FOUND |
DMUS_E_TRACK_NOT_FOUND |
E_POINTER |
Remarks
All bands and waveform data in the segment are downloaded.
Always call IDirectMusicSegment8::Unload before releasing
the segment.
仅当音乐文件为MIDI文件的时候,才需要调用这个函数,因为该函数会改变音乐文件的信息,如果在不适当的时候强制调用该函数,会导致主音轨改变或者丢失。如果要改变音色库(比如重新指定一个新的DLS给一个段音乐),必须首先卸载音色库数据,然后再加载新的音色库,并重新播放音乐。
以下代码示例了如何使用Download。
// downloads band data to a performance
if(FAILED(g_dm_segment->Download(g_dm_performance)))
return FALSE;
当完成播放或者切换音色库时,必须调用一个卸载函数IDirectMusicSegment8::Unload,用它释放音色库及其他资源。
g_pDMSegment->Unload(g_pDS);
循环和重复
在播放之前最后一个要做的工作是设置重复点和重复次数。例如有一小段音乐,希望重复播放这段音乐里的一小部分,这时就需要设置循环起始点和结束点,然后设置重复播放次数,如下图所示:
![](http://www.cppblog.com/images/cppblog_com/lovedday/4157/o_loop_repeat.jpg)
设置循环点是IDirectMusicSegment8::SetLoopPoints函数的职责。
The SetLoopPoints method sets the start and end points of
the part of the segment that repeats the number of times set by the
IDirectMusicSegment8::SetRepeats method.
Syntax
HRESULT SetLoopPoints(
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd
);
Parameters
mtStart
Point at which to begin the loop.
mtEnd
Point at which to end the loop. A value of 0 loops the entire
segment.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return DMUS_E_OUT_OF_RANGE.
Remarks
When the segment is played, it plays from the segment start time until
mtEnd, then loops to mtStart, plays the looped portion the number
of times set by IDirectMusicSegment8::SetRepeats, and then
plays to the end.
The default values are set to loop the entire segment from beginning to end.
The method fails if mtStart is greater than or equal to the length
of the segment, or if mtEnd is greater than the length of the segment.
If mtEnd is 0, mtStart must be 0 as well.
This method does not affect any currently playing segment states created from
this segment.
The loop points of a cached segment persist even if the segment is released,
and then reloaded. To ensure that a segment is not subsequently reloaded from
the cache, call IDirectMusicLoader8::ReleaseObject on it before
releasing it.
很多情况下,我们希望重复播放整首歌曲,这个时候就不需要使用SetLoopPoints函数。设置完循环点后,就可以设置重复次数了(当然,先设置重复次数也是可以的)。如果希望音乐播放一次后就停止,设置重复次数为0,如果希望将曲目播放两次,需要将重复次数设置为1。设置重复次数使用
IDirectMusicSegment8::SetRepeats函数,这个函数只有一个参数,就是曲目重复的次数,如果将这个值设置为
DMUS_SEG_REPEAT_INFINITE,那么播放就会一直持续。
The SetRepeats method sets the number of times the looping
portion of the segment is to repeat.
Syntax
HRESULT SetRepeats(
DWORD dwRepeats
);
Parameters
dwRepeats
Number of times that the looping portion of the segment is to
repeat, or DMUS_SEG_REPEAT_INFINITE to repeat until explicitly stopped. A value
of 0 specifies a single play with no repeats.
Return Values
The method returns S_OK.
播放和停止播放
如果要让演奏器播放音乐片段,只需要调用 IDirectMusicPerformance8::PlaySegmentEx函数就可以了。
The PlaySegmentEx method begins playback of a segment. The
method offers greater functionality than
IDirectMusicPerformance8::PlaySegment.
Syntax
HRESULT PlaySegmentEx(
IUnknown* pSource,
WCHAR *pwzSegmentName,
IUnknown* pTransition,
DWORD dwFlags,
__int64 i64StartTime,
IDirectMusicSegmentState** ppSegmentState,
IUnknown* pFrom,
IUnknown* pAudioPath
);
Parameters
pSource
Address of the IUnknown interface of the
object to play.
pwzSegmentName
Reserved. Set to NULL.
pTransition
IUnknown interface pointer of a template
segment to use in composing a transition to this segment. Can be NULL. See
Remarks.
dwFlags
Flags that modify the method's behavior. See
DMUS_SEGF_FLAGS.
i64StartTime
Performance time at which to begin playing the segment,
adjusted to any resolution boundary specified in dwFlags. The time is
in music time unless the DMUS_SEGF_REFTIME flag is set. A value of zero causes
the segment to start playing as soon as possible.
ppSegmentState
Address of a variable that receives an
IDirectMusicSegmentState interface pointer for this instance of the
playing segment. Use QueryInterface to obtain
IDirectMusicSegmentState8. The reference count of the interface is
incremented. This parameter can be NULL if no segment state pointer is wanted.
pFrom
IUnknown interface pointer of a segment state
or audiopath to stop when the new segment begins playing. If it is an audiopath,
all segment states playing on that audiopath are stopped. This value can be
NULL. See Remarks.
pAudioPath
IUnknown interface pointer of an object that
represents the audiopath on which to play, or NULL to play on the default path.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_AUDIOPATH_INACTIVE |
DMUS_E_AUDIOPATH_NOPORT |
DMUS_E_NO_MASTER_CLOCK |
DMUS_E_SEGMENT_INIT_FAILED |
DMUS_E_TIME_PAST |
E_OUTOFMEMORY |
E_POINTER |
如果希望停止播放,只需要调用IDirectMusicPerformance8::Stop函数就可以了。
The Stop method stops playback of a segment or segment
state.
This method has been superseded by IDirectMusicPerformance8::StopEx,
which can stop playback of a segment, segment state, or audiopath.
Syntax
HRESULT Stop(
IDirectMusicSegment* pSegment,
IDirectMusicSegmentState* pSegmentState,
MUSIC_TIME mtTime,
DWORD dwFlags
);
Parameters
pSegment
Segment to stop playing. All segment states based on this
segment are stopped at mtTime. See Remarks.
pSegmentState
Segment state to stop playing. See Remarks.
mtTime
Time at which to stop the segment, segment state, or both. If
the time is in the past or if 0 is passed in this parameter, the specified
segment and segment states stop playing immediately.
dwFlags
Flag that indicates when the stop should occur. Boundaries are
in relation to the current primary segment. For a list of values, see
IDirectMusicPerformance8::StopEx.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return E_POINTER.
Remarks
If pSegment and pSegmentState are both NULL, all music
stops, and all currently cued segments are released. If either pSegment
or pSegmentState is not NULL, only the requested segment states are
removed from the performance. If both are non-NULL and DMUS_SEGF_DEFAULT is
used, the default resolution from the pSegment is used.
If you set all parameters to NULL or 0, everything stops immediately, and
controller reset messages and note-off messages are sent to all mapped
performance channels.
卸载音乐数据
使用完音乐之后,第一件要做的事就是卸载音色库数据,可以通过IDirectMusicSegment8::Unload来完成。
The Unload method unloads instrument data from a performance
or audiopath.
Syntax
HRESULT Unload(
IUnknown *pAudioPath
);
Parameters
pAudioPath
Pointer to the IUnknown interface of the
performance or audiopath from which to unload the instrument data.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_TRACK_NOT_FOUND |
E_POINTER |
Remarks
The method succeeds even if no data was previously downloaded.
还要释放加载器中的高速缓存数据,通过IDirectMusicLoader8::ReleaseObjectByUnknown来实现。
The ReleaseObjectByUnknown method releases the loader's
reference to an object. This method is similar to
IDirectMusicLoader8::ReleaseObject and is suitable for releasing
objects for which the IDirectMusicObject8 interface is not
readily available.
Syntax
HRESULT ReleaseObject(
IUnknown * pObject
);
Parameters
pObject
Address of the IUnknown interface pointer of
the object to release.
Return Values
If the method succeeds, the return value is S_OK, or S_FALSE if the object
has already been released or cannot be found in the cache.
If it fails, the method can return E_POINTER.
如果开始的时候加载了音色库,现在是时候从加载器对象中将它卸载掉了,就像刚才从加载器中卸载音乐片段对象一样。另外,清空缓存很重要,有一个函数可以强制清空整个缓存区,但是并不需要在卸载加载器前调用它,因为在卸载过程中这个操作是自动完成的。
The ClearCache method removes all saved references to a
specified object type.
Syntax
HRESULT ClearCache(
REFGUID rguidClass
);
Parameters
rguidClass
Reference to (C++) or address of (C) the identifier of the
class of objects to clear, or GUID_DirectMusicAllTypes to clear all types. For a
list of standard loadable classes, see IDirectMusicLoader8.
Return Values
The method returns S_OK.
Remarks
This method clears all objects that are currently being held, but does not
turn off caching. Use the IDirectMusicLoader8::EnableCache
method to turn off automatic caching.
To clear a single object from the cache, call the
IDirectMusicLoader8::ReleaseObject method.
修改音乐
在使用DirectMusic缓冲的时候,我们可以在播放音乐的时候对音乐做很多处理,比如通过DirectSound声音缓冲对象改变音量、节拍、加入特殊效果等。
音量设置
我们可以改变两个音量设置。第一个是演奏器的音量(整个音乐系统的音量),第二个是每个音乐片段对象的回放音量。如下图所示,当每个音乐片段被加载到演奏器中的时候,音乐片段的音量会被修改,演奏器的音量又被全局音量所影响。
![](http://www.cppblog.com/images/cppblog_com/lovedday/4157/o_sound_volume.jpg)
演奏器音量(主音量)是一个全局参数,调用IDirectMusicPerformance8::SetGlobalParam设置。
The SetGlobalParam method sets global values for the
performance.
Syntax
HRESULT SetGlobalParam(
REFGUID rguidType,
void* pParam,
DWORD dwSize
);
Parameters
rguidType
Reference to (C++) or address of (C) the identifier of the
type of data.
pParam
Address of data to be copied and stored by the performance.
dwSize
Size of the data. This is constant for each rguidType.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
E_FAIL |
E_POINTER |
E_OUTOFMEMORY |
Remarks
The dwSize parameter is needed because the performance does not know
about all types of data. New types can be created as needed.
For the parameters defined by DirectMusic and their associated data types,
see Setting and Retrieving Global Parameters.
参数rguidType指定允许的GUID类型如下:
By using the IDirectMusicPerformance8::SetGlobalParam and
IDirectMusicPerformance8::GetGlobalParam methods, you can set
and retrieve parameters that affect the entire performance rather than a single
track.
The parameter to be set or retrieved is identified by a GUID in the
rguidType parameter of the method. Each parameter is associated with a
particular data type, whose size is given in the dwSize parameter. The
predefined GUIDs and their data types are shown in the following table.
Parameter type GUID
(rguidType) |
Data
(*pParam) |
Description |
GUID_PerfAutoDownload |
BOOL |
This
parameter controls whether instruments are automatically downloaded when
a segment is played. By default, it is off. See Downloading and
Unloading Bands. |
GUID_PerfMasterGrooveLevel |
char |
The master
groove level is a value that is always added to the
groove level established by the command track. The resulting value is
adjusted, if necessary, to fall within the range from 1 through 100. |
GUID_PerfMasterTempo |
float |
The master
tempo is a scaling factor applied to the tempo by the final output tool.
By default, it is 1. A value of 0.5 would halve the tempo, and a value
of 2.0 would double it. This value can be set in the range from
DMUS_MASTERTEMPO_MIN through DMUS_MASTERTEMPO_MAX. |
GUID_PerfMasterVolume |
long |
The master
volume is an amplification or attenuation factor, in hundredths of a
decibel, applied to the default volume of the entire performance and any
other performances using the same synthesizer. The range of permitted
values is determined by the port. For the default software synthesizer,
the allowed range is +20db to -200dB, but the useful range is +10db to
-100db. Hardware MIDI ports do not support changing master volume.
Setting this parameter is equivalent to calling
IKsControl::KsProperty for the GUID_DMUS_PROP_Volume property
set on every port in the performance. |
Applications can also use custom types of global parameters. To create a new
type, establish a GUID and a data type for it.
Note All parameters have to be set before
they can be retrieved. When a parameter is set, the performance allocates memory
for the data in a linked list of items that are identified by GUID. If
SetGlobalParam has never been called on the parameter, it does not
appear in this linked list, and GetGlobalParam fails.
为了简化音量的操作,我们不使用分贝来设置音量,而是使用百分数。百分数的范围从0 -
100,0表示没有声音,100表示最大音量。最大音量的时候对音乐有少许的放大效果,有些时候这个放大效果会使得音乐发生扭曲,所以设置音量的时候需要格外小心。
下面这个小函数的作用就是用百分数来设置主音量:
//--------------------------------------------------------------------------------
// Set volume for performance.
//--------------------------------------------------------------------------------
BOOL set_master_volume(long level)
{
// get volume range and calculate volume
long range = labs(DMUS_VOLUME_MAX - DMUS_VOLUME_MIN);
long volume = DMUS_VOLUME_MIN + range / 100 * level;
// set volume for performance
if(FAILED(g_dm_performance->SetGlobalParam(GUID_PerfMasterVolume, &volume, sizeof(long))))
return FALSE;
return TRUE;
}
如果要对音乐片段设置音量,需要获取对音频通道接口的控制。因为在播放音乐的时候已经创建了音频通道,现在只需要用函数将这个对象获取即可,可以通过调用IDirectMusicPerformance8::GetDefaultAudioPath来实现。
The GetDefaultAudioPath method retrieves the default
audiopath set by IDirectMusicPerformance8::InitAudio or
IDirectMusicPerformance8::SetDefaultAudioPath.
Syntax
HRESULT GetDefaultAudioPath(
IDirectMusicAudioPath** ppAudioPath
);
Parameters
ppAudioPath
Address of a variable that receives the
IDirectMusicAudioPath8 interface pointer of the default audiopath.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_AUDIOPATHS_NOT_VALID |
DMUS_E_NOT_INIT |
E_POINTER |
一旦获得了指向IDirectMusicAudioPath8对象的指针,就能使用IDirectMusicAudioPath8::SetVolume函数修改变量了。
The SetVolume method sets the audio volume on the audiopath.
The volume can be faded in or out.
Syntax
HRESULT SetVolume(
long lVolume,
DWORD dwDuration
);
Parameters
lVolume
Value that specifies the attenuation, in hundredths of a
decibel. This value must be in the range from -9600 to 0. Zero is full volume.
dwDuration
Value that specifies the time, in milliseconds, over which the
volume change takes place. A value of 0 ensures maximum efficiency.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_NOT_INIT |
E_INVALIDARG |
Remarks
This method works by sending a volume curve message. Any
volume events occurring later, such as a band change, override the volume set by
this method. IDirectMusicAudioPath8::SetVolume is useful mainly
for adjusting currently playing sounds; for example, to fade out before stopping
a segment. If you want to make a global change that affects all playback, use
one of the following techniques:
- Obtain the buffer object from the audiopath and use
IDirectSoundBuffer8::SetVolume.
- Obtain the port object from the audiopath and use
IKsControl::KsProperty to change the GUID_DMUS_PROP_Volume property
set.
- Set the master volume for the performance. See Setting and Retrieving
Global Parameters.
下面这个函数使用0-100之间的数值来设置音乐片段的音量,100表示音量最大,0表示静音。
//--------------------------------------------------------------------------------
// Set volume for DirectMusic segment.
//--------------------------------------------------------------------------------
BOOL set_segment_volume(long level)
{
IDirectMusicAudioPath8* audio_path;
// get pointer to audio path
if(FAILED(g_dm_performance->GetDefaultAudioPath(&audio_path)))
return FALSE;
// calculate audio volume and set volume for audio path
long volume = -96 * (100 - level);
if(FAILED(audio_path->SetVolume(volume, 0)))
{
audio_path->Release();
return FALSE;
}
return TRUE;
}
改变节拍
想象一下拥有改变音乐节拍的魔法。比如,当玩家靠近一个怪物,正要引发一场激烈战斗的时候,节奏突然变快了,游戏者立刻就能知道他已经进入了麻烦之中。这一切很不错,很让人激动,而这一切在DirectMusic实现起来并不困难。
在DirectMusic中,节奏速度的度量单位通常是每分钟多少个拍子(beats
per minute BPM)。一般来说这个数值通常会是120,我们有很多方法可以改变节拍速度,最简单的方法就是使用一个缩放因子调节演奏器的主节拍速度。比如设置缩放因子为0.5,使得拍子的速度变为正常拍子的一半,设置缩放因子为2使得拍子速度变为正常拍子的两倍。
下面这个函数修改演奏器的节拍,取值从0 - 100。
//--------------------------------------------------------------------------------
// Set tempo for DirectMusic performance.
//--------------------------------------------------------------------------------
BOOL set_tempo(long level)
{
float tempo = (float) level / 100.0f;
if(FAILED(g_dm_performance->SetGlobalParam(GUID_PerfMasterTempo, &tempo, sizeof(float))))
return FALSE;
return TRUE;
}
需要注意,改变节拍速度不是立即生效的,需要过几秒钟。另外需要记住的是set_tempo
函数影响的是全局节拍速度,也就是说所有音乐的节拍速度都被修改了,所以当播放完歌曲后,需要将拍子设置回原来的值。
获取声道控制
在这么多音乐效果的特性里面,最后获取的是DirectSound缓存对象,因为DirectSound缓存是音色库合成音乐的关键所在,所以能对它做很多事情。想要获取缓存,首先可以获取音频通道对象,然后用它来获取音频缓冲区对象。
在下图中,可以看到音乐数据从演奏器中通过音频通道合成音乐的一个默认流程,我们可以在这个流程的任何步骤中截取并修改。
![](http://www.cppblog.com/images/cppblog_com/lovedday/4157/o_sound_channel.jpg)
获取音频通道是函数IDirectMusicAudioPath8::GetObjectInPath的工作。
The GetObjectInPath method retrieves an interface for an
object in the audiopath.
Syntax
RESULT GetObjectInPath(
DWORD dwPChannel,
DWORD dwStage,
DWORD dwBuffer,
REFGUID guidObject,
DWORD dwIndex,
REFGUID iidInterface,
void ** ppObject
);
Parameters
dwPChannel
Performance channel to search, or
DMUS_PCHANNEL_ALL to search all channels. The first channel is numbered 0. (See
Remarks.)
dwStage
Stage in the audiopath. Can be one of the values in the
following table.
Value |
Description |
DMUS_PATH_AUDIOPATH_GRAPH |
Get the
audiopath toolgraph. One is created if none exists. |
DMUS_PATH_AUDIOPATH_TOOL |
Get a tool
from the audiopath toolgraph. |
DMUS_PATH_BUFFER |
Get a
DirectSound buffer. |
DMUS_PATH_BUFFER_DMO |
Get a
DMO in a buffer. |
DMUS_PATH_MIXIN_BUFFER |
Get a
global mix-in buffer. |
DMUS_PATH_MIXIN_BUFFER_DMO |
Get a DMO
in a global mix-in buffer. |
DMUS_PATH_PERFORMANCE |
Get the
performance. |
DMUS_PATH_PERFORMANCE_GRAPH |
Get the
performance toolgraph. One is created if none exists. |
DMUS_PATH_PERFORMANCE_TOOL |
Get a tool
from the performance toolgraph. |
DMUS_PATH_PORT |
Get the
synthesizer. |
DMUS_PATH_PRIMARY_BUFFER |
Get the
primary buffer. |
dwBuffer
Index of the buffer (if dwStage is DMUS_PATH_BUFFER
or DMUS_PATH_MIXIN_BUFFER), or index of the buffer in which the DMO resides (if
dwStage is DMUS_PATH_BUFFER_DMO or DMUS_PATH_MIXIN_BUFFER_DMO).
guidObject
Class identifier of the object, or GUID_All_Objects to search
for an object of any class. This parameter is ignored if only a single class of
object can exist at the stage specified by dwStage, and can be set to
GUID_NULL.
dwIndex
Index of the object within a list of matching objects. Set to
0 to find the first matching object. If dwStage is DMUS_PATH_BUFFER or
DMUS_PATH_MIXIN_BUFFER, this parameter is ignored, and the buffer index is
specified by dwBuffer.
iidInterface
Identifier of the desired interface, such as
IID_IDirectMusicTool.
ppObject
Address of a variable that receives a pointer to the requested
interface.
Return Values
If the method succeeds, the return value is S_OK.
If it fails, the method can return one of the error values shown in the
following table.
Return code |
DMUS_E_NOT_FOUND |
E_INVALIDARG |
E_OUTOFMEMORY |
E_NOINTERFACE |
E_POINTER |
Remarks
The value in dwPChannel must be 0 for any stage that is not
channel-specific. Objects in the following stages are channel-specific and can
be retrieved by setting a channel number or DMUS_PCHANNEL_ALL in dwPChannel:
DMUS_PATH_AUDIOPATH_TOOL
DMUS_PATH_BUFFER
DMUS_PATH_BUFFER_DMO
DMUS_PATH_PERFORMANCE_TOOL
DMUS_PATH_PORT
The precedence of the parameters in filtering out unwanted objects is as
follows:
- dwStage.
- guidObject. If this value is not GUID_All_Objects, only objects
whose class identifier equals guidObject are searched. However,
this parameter is ignored for stages where only a single class of object can
exist, such as DMUS_PATH_AUDIOPATH_GRAPH.
- dwPChannel. If the stage is channel-specific and this value is
not DMUS_PCHANNEL_ALL, only objects on the channel are searched.
- dwBuffer. This is used only if dwStage is
DMUS_PATH_BUFFER, DMUS_PATH_MIXIN_BUFFER, DMUS_PATH_BUFFER_DMO, or
DMUS_PATH_MIXIN_BUFFER_DMO.
- dwIndex.
If a matching object is found but the interface specified by iidInterface
cannot be obtained, the method fails.
The following example function shows how to enumerate the buffers in an
audiopath:
void DumpAudioPathBuffers(IDirectMusicAudioPath *pDirectMusicAudioPath)
{
DWORD dwBuffer = 0;
IDirectSoundBuffer *pDirectSoundBuffer;
while (S_OK == pDirectMusicAudioPath->GetObjectInPath(
DMUS_PCHANNEL_ALL, DMUS_PATH_BUFFER, dwBuffer,
GUID_NULL, 0, IID_IDirectSoundBuffer,
(void**) &pDirectSoundBuffer))
{
// Do something with pDirectSoundBuffer.
// . . .
dwBuffer++;
pDirectSoundBuffer->Release();
}
}
下面这个函数从演奏器获取默认的音频通道对象,并返回一个可以演奏的IDirectSoundBuffer8对象。
//--------------------------------------------------------------------------------
// Get DirectSound buffer from audio path.
//--------------------------------------------------------------------------------
IDirectSoundBuffer8* get_sound_buffer()
{
IDirectMusicAudioPath8* audio_path;
IDirectSoundBuffer* ds_buffer;
IDirectSoundBuffer8* ds_buffer8;
// get autio path
if(FAILED(g_dm_performance->GetDefaultAudioPath(&audio_path)))
return NULL;
// create DirectSound buffer
if(FAILED(audio_path->GetObjectInPath(DMUS_PCHANNEL_ALL, DMUS_PATH_BUFFER, 0, GUID_NULL, 0,
IID_IDirectSoundBuffer, (LPVOID*) &ds_buffer)))
{
audio_path->Release();
return FALSE;
}
audio_path->Release();
// query interface to DirectSound buffer8
if(FAILED(ds_buffer->QueryInterface(IID_IDirectSoundBuffer8, (void**) &ds_buffer8)))
{
ds_buffer->Release();
return FALSE;
}
ds_buffer->Release();
return ds_buffer8;
}
以下给出一个完整的示例,来演示如何加载和播放MIDI音乐。
点击下载源码和工程
完整源码示例:
/***************************************************************************************
PURPOSE:
Midi Playing Demo
***************************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <dsound.h>
#include <dmusici.h>
#include "resource.h"
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dsound.lib")
#pragma warning(disable : 4996)
#define Safe_Release(p) if((p)) (p)->Release();
// window handles, class.
HWND g_hwnd;
char g_class_name[] = "MidiPlayClass";
IDirectSound8* g_ds; // directsound component
IDirectMusicPerformance8* g_dm_performance; // directmusic performance
IDirectMusicLoader8* g_dm_loader; // directmusic loader
IDirectMusicSegment8* g_dm_segment; // directmusic segment
//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return (long) DefWindowProc(hwnd, msg, wParam, lParam);
}
//--------------------------------------------------------------------------------
// Load DirectMusic collection object.
//--------------------------------------------------------------------------------
IDirectMusicCollection8* Load_DLS_Collection(char* filename)
{
DMUS_OBJECTDESC dm_obj_desc;
IDirectMusicCollection8* dm_collection;
// get the object
ZeroMemory(&dm_obj_desc, sizeof(DMUS_OBJECTDESC));
dm_obj_desc.dwSize = sizeof(DMUS_OBJECTDESC);
dm_obj_desc.guidClass = CLSID_DirectMusicCollection;
dm_obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
// Converts a sequence of multibyte characters to a corresponding sequence of wide characters
mbstowcs(dm_obj_desc.wszFileName, filename, MAX_PATH);
// retrieves an object from a file or resource and returns the speficied interface
if(FAILED(g_dm_loader->GetObject(&dm_obj_desc, IID_IDirectMusicCollection8, (LPVOID*)&dm_collection)))
return NULL;
return dm_collection;
}
//--------------------------------------------------------------------------------
// Play midi file which specified with filename.
//--------------------------------------------------------------------------------
BOOL Play_Midi(char* filename)
{
DMUS_OBJECTDESC dm_obj_desc;
// get the object
ZeroMemory(&dm_obj_desc, sizeof(DMUS_OBJECTDESC));
dm_obj_desc.dwSize = sizeof(DMUS_OBJECTDESC);
dm_obj_desc.guidClass = CLSID_DirectMusicSegment;
dm_obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
// Converts a sequence of multibyte characters to a corresponding sequence of wide characters
mbstowcs(dm_obj_desc.wszFileName, filename, MAX_PATH);
// retrieves an object from a file or resource and returns the speficied interface
if(FAILED(g_dm_loader->GetObject(&dm_obj_desc, IID_IDirectMusicSegment8, (LPVOID*)&g_dm_segment)))
return FALSE;
// setup midi playing
if(strstr(filename, ".mid"))
{
// set data on a track inside the segment
if(FAILED(g_dm_segment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
// downloads band data to a performance
if(FAILED(g_dm_segment->Download(g_dm_performance)))
return FALSE;
// set to loop forever
g_dm_segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
// play on default audio path
g_dm_performance->PlaySegmentEx(g_dm_segment, NULL, NULL, 0, 0, NULL, NULL, NULL);
return TRUE;
}
//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
WNDCLASS win_class;
MSG msg;
// create window class and register it
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = Window_Proc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = DLGWINDOWEXTRA;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(inst, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = g_class_name;
if(! RegisterClass(&win_class))
return FALSE;
// create the main window
g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_MIDIPLAY), 0, NULL);
ShowWindow(g_hwnd, cmd_show);
UpdateWindow(g_hwnd);
// initialize and configure directsound
// creates and initializes an object that supports the IDirectSound8 interface
if(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
{
MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
return 0;
}
// set the cooperative level of the application for this sound device
g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);
// initialize COM
//
// initialize the COM library on the current thread and identifies the concurrency model as single-thread
// apartment (STA).
CoInitialize(0);
// create the DirectMusic performance object
//
// creates a single uninitialized object of the class associated with a specified CLSID.
CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8,
(void**)&g_dm_performance);
// create the DirectMusic loader object
CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, (void**)&g_dm_loader);
// initialize the performance with the standard audio path.
// this initialize both directmusic and directsound and sets up the synthesizer.
g_dm_performance->InitAudio(NULL, NULL, g_hwnd, DMUS_APATH_SHARED_STEREOPLUSREVERB, 128, DMUS_AUDIOF_ALL, NULL);
// tell directmusic where the default search path is
char path[MAX_PATH];
WCHAR search_path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, path);
// maps a character string to a wide-character (Unicode) string
MultiByteToWideChar(CP_ACP, 0, path, -1, search_path, MAX_PATH);
// set a search path for finding object files
g_dm_loader->SetSearchDirectory(GUID_DirectMusicAllTypes, search_path, FALSE);
// play midi
Play_Midi("escape.mid");
// start message pump, waiting for signal to quit.
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// release directsound objects
if(g_dm_segment)
g_dm_segment->Unload(g_ds);
if(g_dm_loader)
g_dm_loader->ReleaseObjectByUnknown(g_dm_segment);
if(g_dm_segment)
g_dm_segment->Release();
if(g_ds)
g_ds->Release();
UnregisterClass(g_class_name, inst);
// release COM system
//
// Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other
// resources that the thread maintains, and forces all RPC connections on the thread to close.
CoUninitialize();
return (int) msg.wParam;
}
运行截图:
阅读下篇:用DirectX
Audio和DirectShow播放声音和音乐(7)