天行健 君子当自强而不息

用DirectX Audio和DirectShow播放声音和音乐(6)


本篇是 用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的时候,就会自动用新修改的乐器覆盖住内存中的旧乐器,如下图所示:

当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);

 

循环和重复

在播放之前最后一个要做的工作是设置重复点和重复次数。例如有一小段音乐,希望重复播放这段音乐里的一小部分,这时就需要设置循环起始点和结束点,然后设置重复播放次数,如下图所示:

设置循环点是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声音缓冲对象改变音量、节拍、加入特殊效果等。

 

音量设置

我们可以改变两个音量设置。第一个是演奏器的音量(整个音乐系统的音量),第二个是每个音乐片段对象的回放音量。如下图所示,当每个音乐片段被加载到演奏器中的时候,音乐片段的音量会被修改,演奏器的音量又被全局音量所影响。

演奏器音量(主音量)是一个全局参数,调用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缓存是音色库合成音乐的关键所在,所以能对它做很多事情。想要获取缓存,首先可以获取音频通道对象,然后用它来获取音频缓冲区对象。

在下图中,可以看到音乐数据从演奏器中通过音频通道合成音乐的一个默认流程,我们可以在这个流程的任何步骤中截取并修改。

获取音频通道是函数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:

  1. dwStage.
  2. 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.
  3. dwPChannel. If the stage is channel-specific and this value is not DMUS_PCHANNEL_ALL, only objects on the channel are searched.
  4. 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.
  5. 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)
 

posted on 2007-07-31 01:42 lovedday 阅读(3798) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论