(非常不好意思,我前面传的代码有点小问题,已修正,VS2010+WINDOWS7旗舰版测试通过2013.10.29)
自己喜欢游戏,就想做游戏去,但是不想直接完全用人家现成的引擎。
游戏引擎的大头光影和物理引擎部分咱不奢求了,但基本的编写一个游戏的流程和原理得知道吧?
遂决定自学DirectX,前面的那些DirectX初始化啊,建立顶点缓存,世界变换什么的废话我也不多说了,这篇文章的中心是讲怎样播放声音的。我本人基础很薄弱,所以在这里写的详细点,术语也经常弄不懂,求指正。
一、mpg123解码器的选择,编译,以及如何添加到工程中
首先,就我所知,貌似没有一个音频播放lib或者dll能直接播放声音文件的,必须得经过解码(.mp3,.ogg,etc.),即使是.wav格式读进来的二进制流要播放也得经过一番转换
.mp3格式最普遍,我们就来讲讲.mp3格式。
解码器有很多选择,最近mpg123更新了,我就觉得它很与时俱进,听闻千千静听(现百度音乐)用的就是mpg123,那我也就用了mpg123
官网见www.mpg123.de,我用的版本是1.16.0
mpg123是个开源的东西,下下来是一个tar.gz2的文件,用7-zip解压
windows下不太好编译,幸好它提供了VS的工程文件,在它的ports\MSVC++\里面选择一个版本打开
里面提供有2005,2008,2008clr,2010几个版本,不妨说一下我只有2008那个文件夹里面的文件可以编译通过
在调试阶段建议采用debug模式生成的libmpg123,这样断点调试时可以清晰的看到变量的当前值
debug模式生成一个libmpg123.lib
连同src\ibmpg123\文件夹下面的mpg123.h.in
以及ports\MSVC++\的mpg123.h一同拷贝到你的工程文件目录,在VS里面包含到工程中
mpg123.h是为了在windows下调用所定义的一系列属性以及unicode文件名的支持(mpg123_topen)
mpg123.h.in才是各种接口的声明
如果为了Intellisense或者VA_X方便(直接用它提供的mpg123.h和mpg123.h.in没有自动补全提示),可以直接把mpg123.h.in改成mpg123.h单独使用,但是要小改两个地方
#ifndef MPG123_NO_CONFIGURE /* Enable use of this file without configure. */
@INCLUDE_STDLIB_H@
@INCLUDE_SYS_TYPE_H@
这里要改成
#include
#include
然后还要定义一个类型
#define ssize_t SSIZE_T
下面在你的预编译头里面加一句#include "mpg123.h"就行了
我们下面的程序代码主要是用到了mpg123_read,其它的mpg123_init啊,open啊,close啊顾名思义就行
二、DirectSound的简介和使用
LPDIRECTSOUND g_pD3DSound=NULL; //DirectSound的设备指针
LPDIRECTSOUNDBUFFER g_pD3DSoundBuffer=NULL; //指向音乐数据缓存的指针
DSBUFFERDESC dsbd; //音乐数据的缓存格式
void D3DInit(long n)
{
DirectSoundCreate(NULL,&g_pD3DSound,NULL); //创建播放设备
g_pD3DSound->SetCooperativeLevel(hwnd,DSSCL_NORMAL); //设定协同等级,就是缓存的写入级别
memset(&dsbd,0,sizeof(dsbd));
dsbd.dwSize=sizeof(dsbd);
dsbd.dwFlags=DSBCAPS_GLOBALFOCUS; //这么设置保证在后台也能播放
dsbd.dwBufferBytes=n; //解码后的数据大小,以字节计算
dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX));
}
void CleanUp()
{
if(g_pD3DSoundBuffer){g_pD3DSoundBuffer->Release();g_pD3DSoundBuffer=NULL;};
if(g_pD3DSound){g_pD3DSound->Release();g_pD3DSound=NULL;}; //注意顺序
}
#include
using namespace std;
#include
#include "mpg123.h"
#include
#pragma comment (lib,"dsound.lib")
BYTE *buffer;
int err = MPG123_OK; //mpg123的工作状态
long rate=0;
bool hasFile=false;
int channels=0, enc=0;
mpg123_handle *mh=NULL;
LPDIRECTSOUND g_pD3DSound=NULL; //DirectSound的设备指针
LPDIRECTSOUNDBUFFER g_pD3DSoundBuffer=NULL; //指向音乐数据缓存的指针
DSBUFFERDESC dsbd; //音乐数据的缓存格式
WAVEFORMATEX waveform; //当前mp3的音乐格式
HWND hwnd=CreateWindowA("Button","aaa",0,0,0,0,0,0,0,0,0);//一定要创建一个窗口句柄,不然没办法播放
void D3DInit(long n)
{
DirectSoundCreate(NULL,&g_pD3DSound,NULL); //创建播放设备
g_pD3DSound->SetCooperativeLevel(hwnd,DSSCL_NORMAL); //设定协同等级,就是缓存的写入级别
memset(&dsbd,0,sizeof(dsbd));
dsbd.dwSize=sizeof(dsbd);
dsbd.dwFlags=DSBCAPS_GLOBALFOCUS; //这么设置保证在后台也能播放
dsbd.dwBufferBytes=n; //解码后的数据大小,以字节计算
dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX));
}
void CleanUp()
{
//一些清理工作,顺序不要颠倒
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
//此处顺序也不能乱
if(g_pD3DSoundBuffer){g_pD3DSoundBuffer->Release();g_pD3DSoundBuffer=NULL;};
if(g_pD3DSound){g_pD3DSound->Release();g_pD3DSound=NULL;};
}
void Open(CHAR* FileName)
{
//mpg123初始化
mpg123_init();
mh = mpg123_new(NULL, &err);
//检测格式是否正确
mpg123_open(mh,FileName);
//获得每秒采样率,声道数,以及编码格式
mpg123_getformat(mh,&rate,&channels,&enc);
//mpg123只支持这两种MP3编码格式,已经够了,大部分就是这样的
if(enc != MPG123_ENC_SIGNED_16 && enc != MPG123_ENC_FLOAT_32)
{
fprintf(stderr, "Bad encoding: 0x%x!\n", enc);
return;
}
//设置音频的格式
waveform.wFormatTag =WAVE_FORMAT_PCM; //PCM类型的声音
waveform.nChannels =channels; //声道
waveform.nSamplesPerSec =rate; //每秒采样率
waveform.nAvgBytesPerSec=rate*channels*2; //每秒多少字节
waveform.nBlockAlign =4; //我不太清楚,反正4没错
waveform.wBitsPerSample =16; //signed16编码格式的每采样大小
waveform.cbSize =0; //额外信息,一般是0
//告诉系统文件打开正常
hasFile=true;
}
void D3DPlay(long n)
{
LPVOID lpPtr1=new LPVOID;
LPVOID lpPtr2=new LPVOID;
DWORD length1=0,length2=0,state=0;
D3DInit(n); //传递解码后的数据文件大小,便于缓存格式变量创建
dsbd.lpwfxFormat=&waveform; //将读取到的格式传给缓存格式变量
g_pD3DSound->CreateSoundBuffer(&dsbd,&g_pD3DSoundBuffer,NULL);
//打开缓存以便写入,返回两个缓存可写入地址和他们的长度,一般来说第二个地址是没有的
g_pD3DSoundBuffer->Lock(0,n,&lpPtr1,&length1,&lpPtr2,&length2,0);
memcpy(lpPtr1,buffer,n); //拷贝数据
g_pD3DSoundBuffer->Unlock(lpPtr1,length1,0,0); //关闭缓存
g_pD3DSoundBuffer->SetCurrentPosition(0); //设定播放位置从0开始
g_pD3DSoundBuffer->Play(0,0,DSBPLAY_LOOPING); //播放,循环
//等待播放完成
g_pD3DSoundBuffer->GetStatus(&state);
while ((state==1)||(state==5)) //1代表只放一次,5代表循环播放
{
Sleep(200); //每隔200ms检测一次状态
g_pD3DSoundBuffer->GetStatus(&state);
};
printf("Play Over\n");
}
void Play()
{
if(hasFile)
{
long n=mpg123_length(mh)*16*2/8; //通过采样总数*每采样比特数*声道/8获得字节数
size_t done=0;
buffer=(BYTE*)malloc(n);
err=mpg123_read(mh,buffer,n,&done); //读取文件并解码,是的,一句话就完成了
if (err==MPG123_ERR) return;
if ((err==MPG123_DONE)||(err==MPG123_OK)) D3DPlay(done); //若正常解码结束,开始播放
}
}
int main()
{
//读入文件名,只支持英文
CHAR path[30];
cout<<"请输入要播放的mp3文件(带后缀名),请和程序放在同一个目录下"<>path;
Open(path);
Play();
CleanUp();
//不至于播放完了一闪而过
return system("pause");
}