MP3之痛 -- Cocos2dx 游戏移植到 WP8 之路

Cocos2dx是目前最流行的手机游戏引擎之一,开源、轻量、多平台等的诸多特性使得它被很多国内外手游开发者所喜爱。利用Cocos2dx来开发Windows Phone 8的游戏同样也是非常的方便高效。当然任何跨平台的游戏引擎,最终解决的都只能是游戏层面的问题:包括场景的管理、图形的渲染、真实物理世界的模拟等;要想真正在一个平台上把游戏做好,不可避免会遇到很多和平台相关的问题,需要我们每个游戏程序员对于该平台的技术有个比较深入的了解。在Windows Phone 8这个平台上,我希望通过自己的努力帮助大家解决移植过程中遇到的问题。

这一次我们来聊聊mp3音乐播放的问题。在游戏中播放音乐,大家一般使用Cocos2dx自带的CocosDenshion音乐引擎。使用方法相当简单,步骤如下:

  • 工程中加入CocosDension引擎(WP8的目录在cocos2d\cocos\audio\proj.wp8)
  • WinRT工程加入CocosDension的库引用
  • 代码中加入必要的H文件和namespace

#include"SimpleAudioEngine.h"

usingnamespaceCocosDenshion;

SimpleAudioEngine::sharedEngine()->playBackgroundMusic("xxx",true);

SimpleAudioEngine::sharedEngine()->preloadEffect("xxx");

SimpleAudioEngine::sharedEngine()->playEffect("xxx");(注1)

采用上述方法,我们可以很顺利地播放wav格式的音乐。但美中不足的是,因为版权的原因Windows Phone 8的平台上认是不支持mp3格式音乐的播放的,所以CocosDenshion音乐引擎在WP8平台上对mp3格式的文件做了屏蔽。做一下如下试验:我们在上述代码中,把xxx替换为mp3音乐文件名,比如“spacegame.mp3”,运行后抛出如下异常:

抛异常的语句为mediaStreamer.cpp文件的Initialize函数

ThrowIfFailed(ReadChunk(MAKEFOURCC('R','I',21); background:white">'F',21); background:white">'F'),chunkSize,chunkPos));

if(*reinterpret_cast<constDWORD*>(&dataPtr[chunkPos]) !='W',21); background:white">'A',21); background:white">'V',21); background:white">'E'))ThrowIfFailed(E_FAIL);

代码可以看出,CocosDenshion一开始就对mp3的文件进行了过滤,任何非wav的文件都会直接抛出异常。

如何解决这个问题呢?方法有二个。第一个最简单,就是修改资源本身。我们把mp3的格式转换为wav格式,网络上可以找到很多这样的工具。当然因为wav格式的文件体积比较大,所以这个方法不够完美。本文重点介绍第二个方法,也就是使用lamb增加Windows Phone 8平台对于mp3支持

Lamb是目前使用最广泛的MP3编码器,大家可以访问官网获取更多信息:

使用Lamb所提供的解码模块,我们可以在WP8平台上实现mp3格式音乐的播放。我在这里借用一个网络上开源的代码

下载源码后,我们需要用到libmp3lame,libmpghip,src这三个目录夹下的代码。(我已上传代码供大家使用)大家可以把这三块代码拷贝至Cocos2dx的proj.wp8-xaml目录夹下。然后在工程中加入libmp3lame和libmpghip这二个项目。

然后按下述步骤:

1.编译工程,生成libmp3lame.lib,libmpghip.lib

2.在WinRT工程中,加入libmp3lame,libmpghip的库引用

3.为了引用方便,把proj.wp8-xaml\src\include目录下的lame.h文件拷贝到cocos2d\cocos\audio\Include\目录下

4.在cocos2d\cocos\audio\wp8\mediaStreamer.cpp文件的增加头文件引用

"lame.h"

5.在cocos2d\cocos\audio\wp8\mediaStreamer.cpp文件的增加Initialize_MP3()函数,用来初始化mp3文件。源码如下:

voidMediaStreamer::Initialize_MP3(__inconstWCHAR*url)

{

#if1

WCHARfilePath[MAX_PATH] = { 0 };

if((wcslen(url) > 1 &&url[1] ==':'))

// path start with "x:",is absolute path

wcscat_s(filePath,url);

}

elseif(wcslen(url) > 0

&& (L'/'==url[0] || L'\\'==url[0]))

// path start with '/' or '\',is absolute path without driver name

m_locationPath->Data());

// remove '/' or '\\'

constWCHAR*)url[1]);

else

hip_thip=hip_decode_init();

if(!hip)

printf("创建mp3解码失败");

return;

mp3data_structmp3str;//mp3文件编码信息

std::vector<short*>mp3Buffer;// mp3数据流

vector<int>mp3BufferSize;

intsamples;

intmp3_bytes;

intwrite_bytes= 0;

constintBUF_SIZE= 512;

constintINBUF_SIZE= 4096;

constintMP3BUF_SIZE= (int)(1.25 *BUF_SIZE) + 7200;

shortpcm_l[INBUF_SIZE];

shortpcm_r[INBUF_SIZE];

unsignedcharmp3_buf[MP3BUF_SIZE];

FILE*MP3File;

wstringwstr=wstring(filePath);

stringstr_filePath=string(wstr.begin(),wstr.end());

autoerror=fopen_s(&MP3File,str_filePath.c_str(),21); background:white">"rb");

mp3data_structmp3Header;

while((mp3_bytes=fread(mp3_buf,1,210,MP3File)) > 0)

samples=hip_decode_headers(hip,mp3_buf,pcm_l,pcm_r,&mp3Header);

if(samples> 0)

short*tt=newshort[samples*sizeof(short)];

memcpy((void*)tt,(constvoid*)pcm_l,samples*sizeof(short));

mp3Buffer.push_back(tt);

write_bytes+=samples*sizeof(short);

mp3BufferSize.push_back(samples*sizeof(short));

byte*_mp3Buffer=newbyte[write_bytes];

byte*temp=_mp3Buffer;

intsize=mp3BufferSize.size();

for(inti= 0;i<size;i++)

memcpy(temp,mp3Buffer[i],mp3BufferSize[i]);

deletemp3Buffer[i];

temp+=mp3BufferSize[i];

clear();

hip_decode_exit(hip);

m_data.resize(write_bytes);

for(inti= 0;i<write_bytes;i++)

m_data[i] =_mp3Buffer[i];

fclose(MP3File);

m_waveFormat.wFormatTag=WAVE_FORMAT_PCM;//��定

m_waveFormat.nChannels= 1;//固定

m_waveFormat.nSamplesPerSec= (DWORD)mp3Header.samplerate;//固定

m_waveFormat.wBitsPerSample= 16;

m_waveFormat.nBlockAlign=m_waveFormat.nChannels*m_waveFormat.wBitsPerSample/ 8.0;

m_waveFormat.nAvgBytesPerSec=m_waveFormat.nSamplesPerSec*m_waveFormat.nBlockAlign;//越大越快

m_waveFormat.cbSize= 0;

#endif

6.在\cocos2d\cocos\audio\wp8\Audio.cpp文件中,修改PreloadSoundEffect函数。把原来简单的“mediaStreamer.Initialize(CCUtf8ToUnicode(pszFilePath).c_str());语句用如下代码替换,目的是根源不同的音乐格式文件调用相应的初始化函数:

if(m_engineExperiencedCriticalError) {

stringpath(pszFilePath);

intsound=Hash(pszFilePath);

// no MP3 support for CC_PLATFORM_WP8

string::size_typepos=path.find(".mp3");

if(pos!=path.npos)

mediaStreamer.Initialize_MP3(c_str());

else

mediaStreamer.至此大功告成,现在整个工程可以使用CocosDension顺利播放mp3音乐了。由此大家可以看到,这是一种非常好的方案,只需要做很少的代码修改,就实现了WP8平台对于mp3音乐的支持。推荐给大家的同时,也希望大家可以照此继续开发出对于ogg等更多音乐格式的支持的方案。关于这边博文,我借鉴了如下网站的内容。

最后,我认各位读者对于Cocos2dx已是非常的熟悉。如果大家对于Cocos2dx引擎本身或如何在WP8上建立Cocos2dx开发环境不清楚的话,推荐大家去观看我在微软虚拟在线课堂上的免费课程。

谢谢!

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...