本篇是
用DirectX Audio和DirectShow播放聲音和音樂(5)的續(xù)篇。
加載音色庫(樂器)
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的時候,就會自動用新修改的樂器覆蓋住內存中的舊樂器,如下圖所示:

當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;
}
音色庫被加載后,還需要給音樂片段指定音色庫,這個工作通過設置指定的音樂片段的音軌參數(shù)來完成,設置參數(shù)使用函數(shù)
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的配置
一首歌曲如果已經和音色庫一起被完整地加載到了內存中,這首音樂基本上已經可以使用了,唯一存在的問題是因為系統(tǒng)需要進行配置,以便適應一般MIDI文件的配置,所以需要告訴系統(tǒng)加載的文件是否是一個MIDI文件。告訴DirectMusic使用的是MIDI文件,需要再一次調用參數(shù)配置函數(shù)
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函數(shù)來完成操作。
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文件的時候,才需要調用這個函數(shù),因為該函數(shù)會改變音樂文件的信息,如果在不適當?shù)臅r候強制調用該函數(shù),會導致主音軌改變或者丟失。如果要改變音色庫(比如重新指定一個新的DLS給一個段音樂),必須首先卸載音色庫數(shù)據(jù),然后再加載新的音色庫,并重新播放音樂。
以下代碼示例了如何使用Download。
// downloads band data to a performance
if(FAILED(g_dm_segment->Download(g_dm_performance)))
return FALSE;
當完成播放或者切換音色庫時,必須調用一個卸載函數(shù)IDirectMusicSegment8::Unload,用它釋放音色庫及其他資源。
g_pDMSegment->Unload(g_pDS);
循環(huán)和重復
在播放之前最后一個要做的工作是設置重復點和重復次數(shù)。例如有一小段音樂,希望重復播放這段音樂里的一小部分,這時就需要設置循環(huán)起始點和結束點,然后設置重復播放次數(shù),如下圖所示:

設置循環(huán)點是IDirectMusicSegment8::SetLoopPoints函數(shù)的職責。
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函數(shù)。設置完循環(huán)點后,就可以設置重復次數(shù)了(當然,先設置重復次數(shù)也是可以的)。如果希望音樂播放一次后就停止,設置重復次數(shù)為0,如果希望將曲目播放兩次,需要將重復次數(shù)設置為1。設置重復次數(shù)使用
IDirectMusicSegment8::SetRepeats函數(shù),這個函數(shù)只有一個參數(shù),就是曲目重復的次數(shù),如果將這個值設置為
DMUS_SEG_REPEAT_INFINITE,那么播放就會一直持續(xù)。
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函數(shù)就可以了。
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函數(shù)就可以了。
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.
卸載音樂數(shù)據(jù)
使用完音樂之后,第一件要做的事就是卸載音色庫數(shù)據(jù),可以通過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.
還要釋放加載器中的高速緩存數(shù)據(jù),通過IDirectMusicLoader8::ReleaseObjectByUnknown來實現(xiàn)。
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.
如果開始的時候加載了音色庫,現(xiàn)在是時候從加載器對象中將它卸載掉了,就像剛才從加載器中卸載音樂片段對象一樣。另外,清空緩存很重要,有一個函數(shù)可以強制清空整個緩存區(qū),但是并不需要在卸載加載器前調用它,因為在卸載過程中這個操作是自動完成的。
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聲音緩沖對象改變音量、節(jié)拍、加入特殊效果等。
音量設置
我們可以改變兩個音量設置。第一個是演奏器的音量(整個音樂系統(tǒng)的音量),第二個是每個音樂片段對象的回放音量。如下圖所示,當每個音樂片段被加載到演奏器中的時候,音樂片段的音量會被修改,演奏器的音量又被全局音量所影響。

演奏器音量(主音量)是一個全局參數(shù),調用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.
參數(shù)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.
為了簡化音量的操作,我們不使用分貝來設置音量,而是使用百分數(shù)。百分數(shù)的范圍從0 -
100,0表示沒有聲音,100表示最大音量。最大音量的時候對音樂有少許的放大效果,有些時候這個放大效果會使得音樂發(fā)生扭曲,所以設置音量的時候需要格外小心。
下面這個小函數(shù)的作用就是用百分數(shù)來設置主音量:
//--------------------------------------------------------------------------------
// 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;
}
如果要對音樂片段設置音量,需要獲取對音頻通道接口的控制。因為在播放音樂的時候已經創(chuàng)建了音頻通道,現(xiàn)在只需要用函數(shù)將這個對象獲取即可,可以通過調用IDirectMusicPerformance8::GetDefaultAudioPath來實現(xiàn)。
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函數(shù)修改變量了。
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.
下面這個函數(shù)使用0-100之間的數(shù)值來設置音樂片段的音量,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;
}
改變節(jié)拍
想象一下?lián)碛懈淖円魳饭?jié)拍的魔法。比如,當玩家靠近一個怪物,正要引發(fā)一場激烈戰(zhàn)斗的時候,節(jié)奏突然變快了,游戲者立刻就能知道他已經進入了麻煩之中。這一切很不錯,很讓人激動,而這一切在DirectMusic實現(xiàn)起來并不困難。
在DirectMusic中,節(jié)奏速度的度量單位通常是每分鐘多少個拍子(beats
per minute BPM)。一般來說這個數(shù)值通常會是120,我們有很多方法可以改變節(jié)拍速度,最簡單的方法就是使用一個縮放因子調節(jié)演奏器的主節(jié)拍速度。比如設置縮放因子為0.5,使得拍子的速度變?yōu)檎E淖拥囊话耄O置縮放因子為2使得拍子速度變?yōu)檎E淖拥膬杀丁?/span>
下面這個函數(shù)修改演奏器的節(jié)拍,取值從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;
}
需要注意,改變節(jié)拍速度不是立即生效的,需要過幾秒鐘。另外需要記住的是set_tempo
函數(shù)影響的是全局節(jié)拍速度,也就是說所有音樂的節(jié)拍速度都被修改了,所以當播放完歌曲后,需要將拍子設置回原來的值。
獲取聲道控制
在這么多音樂效果的特性里面,最后獲取的是DirectSound緩存對象,因為DirectSound緩存是音色庫合成音樂的關鍵所在,所以能對它做很多事情。想要獲取緩存,首先可以獲取音頻通道對象,然后用它來獲取音頻緩沖區(qū)對象。
在下圖中,可以看到音樂數(shù)據(jù)從演奏器中通過音頻通道合成音樂的一個默認流程,我們可以在這個流程的任何步驟中截取并修改。

獲取音頻通道是函數(shù)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();
}
}
下面這個函數(shù)從演奏器獲取默認的音頻通道對象,并返回一個可以演奏的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)