[收藏]播放MIDI音乐——使用DirectMusic

时间:2022-08-04 05:52:39
( 本文对象: DirectMusic 初学者,想快速知道使用 DirectMusic 播放音乐的人

原作者By Kylinx, 2003-5-15 ,E-mail:game-diy@163.com 

整理:风里有梦


(
转载请保证文档的完整性


DirectX8~9 SDK 中,DirectMusic增加了很多新特性,我在这里单单讲用它播放Midi的部分 

DirectMusic
主要有下面几个部分组成: 

IDirectMusicLoader8 

IDirectMusicPerformance8 

IDirectMusicSegment8 

IDirectMusicSegmentState8 


你也许会问:为什么没有IDirectMusic8 ?这个是因为在DirectMusic中,MicrosoftIDirectMusic8隐藏起来了,也就是说,我们不需要使用这个接口,

可以通过其他方式轻松实现 

IDirectMusic8 

IDirectMusicLoader8 

IDirectMusicPerformance8 

IDirectMusicSegment8 

AudioPath 

IDirectMusicSegmentState8 


IDirectMusicLoader8,
加载器。用于加载Mid文件等等 

IDirectMusicPerformance8
用来控制DirectMusic的接口 

IDirectMusicSegment8
音乐数据段,加载音乐后存放的位置 

IDirectMusicSegmentState8 
音乐段状态 

顺便说一下DLS 

我们知道,mid文件纪录的只是乐器,音调和实践。在播放的时候,声卡对mid文件进行编码,而每种声卡上的波表都有差别,所以在编码的时候,播放出来的声音

就与声卡有关。打个比方,你在A机子直接播放和B机子直接播放同一个mid文件,如果A的声卡和B的声卡不同的话,听起来的音质完全不一样(我们耳朵能听到的只能

是模拟的声音而不是数字化的声音数据)。正因为如此,MicrosoftDirectMusic中使用了DLS,很好的解决了这个问题 

DLS
全称是DownLoadable Sounds 

MSDN
中这样解释: 

A standard for synthesizing wave sounds from digital samples stored in software. The DLS level 1 and level 2 standards are published by the

  MIDI  Manufacturers Association. 

原理就是在DLS文件中通过使用乐器的单音采样来处理这个问题。所以在DirectMusic 

使用默认的DLS文件,把mid音乐转化为数字化的音乐。数字化的声音就可以通过 数字——〉模拟(D/A)转换,随后播放出来的声音听起来都是一样的(因为不需要再声卡上重新编码了啊!) 

好了,说了这么多,该开始应用了 

IDirectMusicLoader8* g_pDMLoader=NULL

IDirectMusicPerformance8* g_pDMPerformance=NULL

IDirectMusicSegment8* g_pSegment=NULL

首先,初始化COM 

(
这个东西博大精深,推荐一本:com本质论!看过这本书后,包你对DirectX的认识有新的飞跃!) 


========================================================================


HRESULT hr = CoInitialize(NULL); 

If( FAILED(hr) ) 
{ ; //
处理错误 

以下为了方便,省了错误处理 


//
下面创建加载器 
CoCreateInstance(CLSID_DirectMusicLoader, //
组件的GUID 
                            NULL, //
不是创建集合 
                            CLSCTX_INPROC, //
创建的环境 
                           IID_IDirectMusicLoader8, //
接口的GUID 
                           (void**)&g_pDMLoader); //
被创建的接口指针 

//
创建控制器
CoCreateInstance(CLSID_DirectMusicPerformance, //
组件的GUID 
                    NULL, //
不是创建集合 
                    CLSCTX_INPROC, //
创建的环境 
                    IID_IDirectMusicPerformance8, //
接口的GUID 
                     (void**)&g_pDMPerformance); //
被创建的接口指针 

//
初始化声音通道 
g_pDMPerformance->InitAudio( 
                    NULL,//DirectMusic
对象的指针,因为不需要我们管理,所以让它自动进行 
                    NULL,//DirectSound
对象的指针,同上 
                    hWnd,//
窗口句柄 
                    DMUS_APATH_SHARED_STEREOPLUSREVERB,//
声音通道(AudioPath)类型:立体声+混响 
                    64, //
音乐通道数 
                    DMUS_AUDIOF_ALL, //
声卡的所有特性 
                    NULL // DMUS_AUDIOPARAMS
对象的指针 
                    ); 

// get current working directory

wchar_t wszDir[MAX_PATH];

   

if ( NULL == _wgetcwd( wszDir, MAX_PATH ) )

        return(-1);

   

//MULTI_TO_WIDE(wszDir, szDir);

   

// tell the loader were to look for files

hr = g_pDMLoader->SetSearchDirectory( GUID_DirectMusicAllTypes,  wszDir,  FALSE );


//
好了,初始化Dmusic完成 
//
下面读入mid文件 


//g_pDMLoader->LoadObjectFromFile( 
                    CLSID_DirectMusicSegment, //
组件的GUID 
                    IID_IDirectMusicSegment8, //
接口的GUID 
                    file;//
文件名,注意用Unicode 
                     (void**) &g_pSegment//
音乐要装到的段 
                    ); 

//填充结构

DMUS_OBJECTDESC ObjDesc;

memset((void*)&ObjDesc, 0, sizeof(DMUS_OBJECTDESC));

ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);

ObjDesc.guidClass = CLSID_DirectMusicSegment;

wcscpy( ObjDesc.wszFileName, pszFile ); // pszFileUNICODE MIDI 文件路径

ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;

//装入MIDIg_pSegment对象

hr = g_pDMLoader->GetObject(&ObjDesc,IID_IDirectMusicSegment, (void**) &g_pSegment);

hr = g_pSegment->SetParam(GUID_StandardMIDIFile, -1, 0, 0, (void*)g_pDMPerformance);

hr = g_pSegment->SetParam(GUID_Download, -1, 0, 0, (void*)g_pDMPerformance);


//
ASCII转换到UNICODE的函数有
//MultiByteToWideChar( 
                    CP_ACP,//ASCII
 
                    0,// 
                    asciifile,//
要转换的ascii字符串 
                    -1,//
要转换的字节数,-1表示以’/ 0’ 结尾的字符串 
                    UnicodeFile,//
转换后UNICODE存放的地方 
                    MAX_FILE_LENGTH); 

//
Runtime


//
下面播放mid音乐 
g_pSegment->SetRepeats(looptimes); //
重复的次数,如果是DMUS_SEG_REPEAT_INFINITE则为无限 


g_pSegment->Download( g_pDMPerformance );//
使用DLS,MID数据转换成数字化的音乐数据 


g_pDMPerformance->PlaySegmentEx(g_pSegment,//
要播放的段 
                    NULL,//
保留,必须为NULL 
                    NULL,//pTransiton 
                    0,//
播放的标志 
                    0,//
开始的位置 
                    NULL,//
用与接收段状态的指针,如果不需要,就为NULL 
                    NULL,//
使用默认 
                    NULL//
默认的AudioPath 
                    ); 

//
暂停播放 
MUSIC_TIME mtime;//MUSIC_TIME就是一个long类型 
g_pDMPerformance->GetTime(NULL, &mtime);//
得到暂停的位置 

//
停止播放 
g_pDMPerformance->Stop(NULL,//
要停止的段,NULL表示全部段都停止 
                    NULL,//
段状态 
                    0,//
多少时间后停止,0表示立即 
                    0//
标志 
                    ); 

//
从暂停点继续播放 

g_pSegment->SetStartPoint(mtime);//
播放点 

//播放
g_pDMPerformance->PlaySegmentEx(g_pSegment, 
                    NULL, 
                    NULL, 
                    DMUS_SEGF_REFTIME, 
                    0, 
                    NULL, 
                    NULL, 
                    NULL 
                    ); 

g_pSegment->SetStartPoint(0); 

//
释放DirectMusic 
g_pDMPerformance->CloseDown(); 
g_pSegment->Release(); 
g_pDMPerformance->Release(); 
g_pDMLoader->Release(); 
CoUninitialize();//
停止使用COM 

好了,整个过程就是这么简单当你理解后,可以自行把上面的代码封装为一个类

这样,你就可以在你的游戏中实现MID的播放啦
当然,播放mid只是DirectMusic功能中很小的一部分 
它还能播放wav文件,sgt文件,实现3D声音等等,具体内容自己参考MSSDK中的文档和例子吧

(
参考资料:  MSDN2005DirectMusic部分)