开发游戏音频程序——播放MIDI文件

开发游戏音频程序——播放MIDI文件

在沉寂了一段时间之后。我开始开发游戏的音频程序了。今天我要为大家展示的是我写的播放MIDI的程序,其实不要以为MIDI的载入和播放很复杂,其实它是相当的简单、只需要选用合适的解码器就行了。现在又几种解码器可以选择。MCI和DirectMusic。其中MCI是windows特别对多媒体应用程序提供的API集合,它可以播放很多流行的格式。但是对MIDI的控制性比较低。而DirectMusic则对MIDI有着较丰富的支持,最典型的就是可以控制节奏(tempo)。

我的代码没有对DirectMusic进行封装,使用C的风格制作的。所以大家看起来很好理解。下面我就来展示我写的代码吧。

Main.h

/*---------------------------------------------------------------------------

蒋轶民制作E-mail:jiangcaiyang123@163.com

最后编辑:年月日:42:29

文件名:main.h

作用:头文件的定义

----------------------------------------------------------------------------*/

/*-----------------------------头文件---------------------------------------*/

#include <dmusici.h>

#include <dmusicf.h>

#include "CWaveFile.h"

// 库文件

#pragma comment( lib, "dxguid.lib" )

/*-----------------------------全局变量-------------------------------------*/

IDirectMusicLoader8* g_pLoader = NULL;

IDirectMusicSegment8* g_pSegment = NULL;

IDirectMusicPerformance8* g_pPerformance = NULL;

IDirectMusicSegmentState* g_pSegmentState = NULL;

IDirectMusicAudioPath8* g_pAudioPath = NULL;

/*------------------------获取控制台窗口句柄----------------------------*/

HWND GetConsoleWindowHandle( void )

{

TCHAR title[512];

HWND hWnd;

GetConsoleTitle( title, sizeof( title ) );

hWnd = FindWindow( NULL, title );

return( hWnd );

}

/*------------------------初始化----------------------------------------*/

void Initialize( void )

{

HWND hConsoleWnd = GetConsoleWindowHandle();

HRESULT hr;// 结果的句柄

// 1、初始化COM组件

hr = CoInitialize( NULL );

ThrowIfFailed( hr, "1、初始化COM组件错误。" );

// 2、创建装载器

hr = CoCreateInstance( CLSID_DirectMusicLoader, NULL,

CLSCTX_INPROC, IID_IDirectMusicLoader8, (void**)&g_pLoader );

ThrowIfFailed( hr, "2、创建装载器错误" );

// 3、创建演艺对象

hr = CoCreateInstance( CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,

IID_IDirectMusicPerformance8, (void**)&g_pPerformance );

ThrowIfFailed( hr, "3、创建演艺对象错误" );

// 4、初始化音频

hr = g_pPerformance->InitAudio( NULL, NULL, hConsoleWnd,

DMUS_APATH_DYNAMIC_STEREO, 128, DMUS_AUDIOF_ALL, NULL );

if ( hr == DSERR_NODRIVER )

{

MessageBox( hConsoleWnd, TEXT( "程序无法定位音频硬件,请确认硬件是否支持DirectX 8。" ), TEXT( "4、初始化音频" ), MB_ICONSTOP );

return;

}

else ThrowIfFailed( hr, "初始化音频错误。" );

return;// 正确返回

}

/*--------------------------释放内存------------------------------------*/

void UnInitialize( void )

{

#define SAFE_RELEASE( p ) if ( p ) { p->Release(); p = NULL; }

if ( g_pPerformance )

{

g_pPerformance->Stop( NULL, NULL, 0, 0 );

g_pPerformance->CloseDown();

SAFE_RELEASE( g_pPerformance );

}

SAFE_RELEASE( g_pLoader );

}

/*--------------------------载入声音------------------------------------*/

void LoadSound( TString filename )

{

HRESULT hr;// 结果的句柄

// 载入文件对象

hr = g_pLoader->LoadObjectFromFile( CLSID_DirectMusicSegment, IID_IDirectMusicSegment8,

(WCHAR*)filename.c_str(), (void**)&g_pSegment );

ThrowIfFailed( hr, "载入文件对象错误。" );

}

/*--------------------------------------------------------------------------*/

bool LoadCWaveFile( CWaveFile& obj ) // 载入CWaveFile数据

{

HRESULT hr; // 结果的句柄

if ( obj.GetData() == 0 )

Throw( "PlayWaveFile:无法获取数据(是不是忘记载入声音文件?)" );

// 设置DMUS_OBJECTDESC结构

DMUS_OBJECTDESC desc;

desc.dwSize = sizeof( DMUS_OBJECTDESC );

desc.dwValidData = DMUS_OBJ_MEMORY | DMUS_OBJ_CLASS;

desc.guidClass = CLSID_DirectMusicSegment;

desc.llMemLength = obj.GetSize();

desc.pbMemData = obj.SaveData();

hr = g_pLoader->GetObject( &desc, IID_IDirectMusicSegment8, (void**)&g_pSegment );

ThrowIfFailed( hr, "PlayWaveFile:无法获取对象。" );

return true;

}

/*--------------------------------------------------------------------------*/

bool Play( void ) // 播放波形文件

{

//g_pSegment->Download( g_pPerformance );

if ( !g_pSegment || !g_pPerformance ) return false;// 无法播放

g_pSegment->Download( g_pPerformance );

g_pPerformance->PlaySegmentEx( g_pSegment, NULL, NULL,

DMUS_SEGF_SECONDARY, 0, (IDirectMusicSegmentState **)&g_pSegmentState, NULL, g_pAudioPath );

return true;

}

/*--------------------------检测声音是否正在播放------------------------*/

bool IsPlaying( void )

{

if ( !g_pSegment || !g_pPerformance ) return false;

return g_pPerformance->IsPlaying( NULL, g_pSegmentState ) == S_OK;

}

/*--------------------------加载资源文件-------------------------------*/

bool LoadWaveResource( HMODULE hMod, TString strResName, WORD resID )

{

HRESULT hr; // 结果的句柄

// 载入资源

HRSRC hRes = FindResource( hMod, MAKEINTRESOURCE( resID ), strResName.c_str() );

if ( hRes == NULL ) Throw( "LoadWaveResource:无法找到资源。" );

HGLOBAL hGlb = LoadResource( hMod, hRes );

if ( hGlb == NULL ) Throw( "LoadWaveResource:无法载入资源。" );

// 设置DMUS_OBJECTDESC结构

DMUS_OBJECTDESC desc;

desc.dwSize = sizeof( DMUS_OBJECTDESC );

desc.dwValidData = DMUS_OBJ_MEMORY | DMUS_OBJ_CLASS;

desc.guidClass = CLSID_DirectMusicSegment;

desc.llMemLength = SizeofResource( hMod, hRes );

desc.pbMemData = (LPBYTE)LockResource( hGlb );

hr = g_pLoader->GetObject( &desc, IID_IDirectMusicSegment8, (void**)&g_pSegment );

ThrowIfFailed( hr, "PlayWaveFile:无法获取对象。" );

return true;

}

/*--------------------------设置全局音量-----------------------------------*/

bool SetGlobalVolume( long decibel )

{

HRESULT hr; // 结果的句柄

long volume = decibel * 100; // 音量在全局中是分贝的倍

hr = g_pPerformance->SetGlobalParam( GUID_PerfMasterVolume, &volume, sizeof( decibel ) );

ThrowIfFailed( hr, "SetGlobalVolume:设置全局声音错误。" );

return true;

}

/*--------------------------设置音量---------------------------------------*/

bool SetVolume( long decibel, DWORD dwDuration )

{

HRESULT hr; // 结果的句柄

long volume = decibel * 100; // 音量在全局中是分贝的倍

// 获得音频的路径,用于设置音量

/*-------------------------------

hr = g_pPerformance->GetDefaultAudioPath( &g_pAudioPath );// 获取默认的音频路径

ThrowIfFailed( hr, "Initialize:无法获取默认音频路径。" );

/*-------------------------------*/

IUnknown* pConfig;

hr = g_pSegment->GetAudioPathConfig( &pConfig );

if ( hr != DMUS_E_NO_AUDIOPATH_CONFIG )// 无法获取音频的配置

{

ThrowIfFailed( hr, "Initialize:在获取音频配置的时候遇到了未知的错误。" );

hr = g_pPerformance->CreateAudioPath( pConfig, TRUE, &g_pAudioPath );

ThrowIfFailed( hr, "Initialize:无法创建音频的路径。" );

}

// 手动创建一个音频的路径

hr = g_pPerformance->CreateStandardAudioPath( DMUS_APATH_DYNAMIC_STEREO, 128/*通道数*/, TRUE, &g_pAudioPath );

ThrowIfFailed( hr, "Initialize:创建音频路径错误。" );

/*-------------------------------*/

g_pSegment->Download( g_pAudioPath );

// 设置音量

hr = g_pAudioPath->SetVolume( volume, dwDuration );

ThrowIfFailed( hr, "SetVolume:设置声音错误。" );

return true;

}

/*--------------------------设置播放的速度(对MIDI有效)-------------------*/

bool SetTempo( int iTempo )

{

HRESULT hr;

DMUS_TEMPO_PARAM tempo;

tempo.dblTempo = iTempo;

hr = g_pSegment->SetParam( GUID_TempoParam, 0xFFFF, 0, 0, &tempo );

ThrowIfFailed( hr, "SetTempo:设置参数错误。" );

// 发送消息

DMUS_TEMPO_PMSG* pTempo;

hr = g_pPerformance->AllocPMsg( sizeof(DMUS_TEMPO_PMSG), (DMUS_PMSG**)&pTempo );

ThrowIfFailed( hr, "SetTempo:分配信息空间错误。" );

// 设置消息结构体

ZeroMemory( pTempo, sizeof(DMUS_TEMPO_PMSG) );

pTempo->dwSize = sizeof(DMUS_TEMPO_PMSG);

pTempo->dblTempo = iTempo;

pTempo->dwFlags = DMUS_PMSGF_REFTIME;

pTempo->dwType = DMUS_PMSGT_TEMPO;

hr = g_pPerformance->SendPMsg( (DMUS_PMSG*)pTempo );

ThrowIfFailed( hr, "SetTempo:发送信息错误。" );

return true;

}

/*--------------------------获取播放的速度(对MIDI有效)-------------------*/

int GetCurrentTempo( void )

{

HRESULT hr;

DMUS_TEMPO_PARAM tempo;

hr = g_pSegment->GetParam( GUID_TempoParam, 0xFFFF, 0, 0, NULL, &tempo );

ThrowIfFailed( hr, "GetCurrentTempo:获取参数错误。" );

return int(tempo.dblTempo);

}

Main.cpp

/*---------------------------------------------------------------------------

蒋轶民制作E-mail:jiangcaiyang123@163.com

最后编辑:年月日:29:06

文件名:main.cpp

作用:头文件的实现

----------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/

// 头文件

#include <iostream>

#include <conio.h>

#include "main.h"

using namespace std;

int main( int, char** )

{

try

{

cout << "初始化设备\n";

Initialize();// 初始化DirectMusic

cout << "载入声音\n";

LoadSound( TEXT( "test.mid" ) ); // 载入声音文件

cout << "播放声音\n";

g_pSegment->SetRepeats( NULL ); // 仅仅播放一次

SetVolume( 0, 0 );

Play();

Sleep( 1000 );

SetTempo( 160 );

cout << "当前播放的速率为" << GetCurrentTempo() << '\n';

while ( IsPlaying() );

UnInitialize(); cout << "释放设备\n";

}

catch( CError& e )

{

e.SaveToFile();

cout << "错误报告已保存。\n";

}

catch( ... )

{

cout << "遇到了未知的错误。\n";

}

return 0;

}

大家可以下载我这个资源,以便完全掌握MIDi的载入和播放方法。

http://download.csdn.net/source/3571213

另外我使用的DirectX SDK的版本是2006年八月的。从下面的链接中可以下载。

http://down.gougou.com/down?cid=8B28FF668DD328D9B3A20A7ED1D3476EFC5AB0D6#com_anchor" target="_blank">http://down.gougou.com/down?cid=8B28FF668DD328D9B3A20A7ED1D3476EFC5AB0D6#com_anchor

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值