mpg123解码MP3,使用DirectSound播放乐曲

(非常不好意思,我前面传的代码有点小问题,已修正,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的简介和使用

DirectSound是DirectXAudio的一个较底层的部件,提供了丰富的接口函数,实现.wav格式的波形声音数据的播放控制。
与一般的WindowsAPI提供的声音播放函数不同,DirectSound可实现多个声音的混合播放。DirectSound可充分使用声卡的内存资源,同时也提供了3D声效算法,模拟出真实的3D立体声。
……以上来自度娘
反正现在你要编个PC或家用机上的大型游戏怎么都得用到DirectX的吧,那干脆就用它提供的DirectSound
手机上的你可以试试其他的API,OPENGL配OPENAL什么的?我不太懂反正
先安装DirectX SDK微软官网下载地址
然后工程中#include和#pragma comment (lib,"dsound.lib")一下
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");
}

四、总结和参考资料
我上面采取的是将整个音频文件载入到缓存里再播放的方式
可能对大文件来说效率不高,但是播放一些小文件来说应该是够了
边载入边播放的例程见http://bbs.csdn.net/topics/310095017
若要详细了解DirectSound请见http://blog.csdn.net/aoosang/article/details/520903   
(我作为一个新手看了挺头大的)

你可能感兴趣的:(mpg123解码MP3,使用DirectSound播放乐曲)