在Visual C++中实现播放mid背景音乐的简单方法

我们经常看到一些小软件、注册机等等都有一些小巧的背景音乐.其实用MIDI音乐具有很多好处:
1.MIDI音乐文件使用更广泛,更容易获得,制作和编辑.
2.DirectMusic附带4MB音色库的Roland软波表,音质也不比XM差。
3.MIDI数据比XM数据更容易被压缩(MIDI文件压缩比在1:8左右).
4.不需要外部编程库,系统自带播放内核,添加的代码量极少.

下面给出一个最简单的MIDI音乐的播放方法,即使用DirectMusic高层库.
虽然利用MCI的高层也可以播放,但它似乎不支持内存载入,
而且无法避免与其他MIDI音乐的同时播放的冲突.
但编译这个程序还需要装上DirectX SDK.

代码如下:——————————————————————————–
// Tiny MIDI player [By Dwing]
#pragma comment(lib,”ole32.lib”)
#pragma comment(lib,”dxguid.lib”)
#pragma comment(linker,”/ENTRY:Entry”)
#include

unsigned char mididata[]={0x4D,0x54,0x68,0x64,……}; // 这里加入mid文件的数据.

IDirectMusicPerformance* performance=0;
IDirectMusicSegment* segment =0;
IDirectMusicLoader* loader =0;

bool PlayMIDI(unsigned char* data,unsigned int size)
{
DMUS_OBJECTDESC desc={0};
desc.dwSize =sizeof(desc);
desc.guidClass =CLSID_DirectMusicSegment;
desc.pbMemData =data;
desc.llMemLength=size;
desc.dwValidData=DMUS_OBJ_CLASS|DMUS_OBJ_MEMORY;
if(FAILED(CoInitialize(0))) return false;
if(FAILED(CoCreateInstance(CLSID_DirectMusicPerformance,0,CLSCTX_INPROC,
IID_IDirectMusicPerformance,(void**)&performance))) return false;
if(FAILED(CoCreateInstance(CLSID_DirectMusicLoader,0,CLSCTX_INPROC,
IID_IDirectMusicLoader,(void**)&loader))) return false;
if(FAILED(loader->GetObject(&desc,IID_IDirectMusicSegment,(void**)&segment))) return

false;
if(FAILED(performance->Init(0,0,0))) return false;
if(FAILED(performance->AddPort(0))) return false;
if(FAILED(segment->SetParam(GUID_StandardMIDIFile,-1,0,0,performance))) return false;
if(FAILED(segment->SetParam(GUID_Download,-1,0,0,performance))) return false;
if(FAILED(segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE))) return false;
if(FAILED(performance->PlaySegment(segment,0,0,0))) return false;
return true;
}

void StopMIDI()
{
if(segment)
{
if(performance)
{
performance->Stop(segment,0,0,0);
segment->SetParam(GUID_Unload,-1,0,0,(void**)performance);
}
segment->Release();
segment=0;
}
if(loader)
{
loader->Release();
loader=0;
}
if(performance)
{
performance->CloseDown();
performance->Release();
performance=0;
}
CoUninitialize();
}

void Entry()
{
if(PlayMIDI(mididata,sizeof(mididata)))
{
MessageBox(0,”Playing…”,”MIDI”,0);
StopMIDI();
}
else
MessageBox(0,”Error!”,”MIDI”,0);
ExitProcess(0);
}
——————————————————————————–

mid文件的数据可用一个小工具自己导出成数组格式.
执行PlayMIDI()后音乐的播放会有约1~2秒的延迟,估计不会有太大影响的.
如果在程序退出前不需要停止播放音乐,可以不调用StopMIDI(),程序会更小.
PlayMIDI()函数用VC的最小代码编译,只有200多字节!

原来那些数据需要手写-_-!汗~~~
写个小程序来自动帮我们写吧!

代码:——————————————————————————–
#include
void main()
{
unsigned char c;
unsigned int n=0;
FILE *fin =fopen(“data.bin”,”rb”);
FILE *fout=fopen(“data.h” ,”wb”);
if(!fin||!fout) {printf(“not found data.bin!n”);return;}
fprintf(fout,”unsigned char *data[]={rn”);
while(1)
{
c=fgetc(fin);
if(feof(fin)) break;
fprintf(fout,”0x%02X,”,c);
if(++n%16==0) fprintf(fout,”rn”);
}
fseek(fout,-1,SEEK_CUR);
fprintf(fout,”rn};rn”);
fclose(fout);
fclose(fin);
}
——————————————————————————–

当然使用资源也是一个好办法,但比起直接使用数组缺点如下:
1.需要调用FineResource,SizeofResource,LoadResource,LockResource等函数,代码量多一些.
2.资源目录表和资源名称(如果使用字符串格式)需要一些额外的数据,且不能被加壳工具压缩.
3.使用资源就非常明显让别人轻易提取出该资源的数据.
4.便于某些无资源编译器或不方便编辑资源的编译器编译(如Dev C++).

还有另一个方法可以直接包含文件,而不是一堆十六进制数据.
但需要有nasm汇编器(把nasmw.exe放入VC编译程序目录中).
工程中添加文件mididata.asm,内容:
section .data
global _mididata
_mididata incbin “midi.mid” ;包含文件的文件名,与此文件同目录
设置mididata.asm的编译属性:
Custom Build/Commands: nasmw -f win32 -o $(OutDir)$(InputName).obj $(InputPath)
Custom Build/Outputs: $(OutDir)$(InputName).obj
然后只需要在需要mididata的C++文件中加入下面一行即可:
extern “C” unsigned char mididata[12345]; //大小需要手动输入

注意:
调用performance->PlaySegment()的前面加上:
segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
这样就可以循环播放.

点赞 (1)

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code