MP3之痛 -- Cocos2dx 游戏移植到 WP8 之路
2015-05-27 10:00
281 查看
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"
using namespace CocosDenshion;
播放BGM的代码
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("xxx", true);
播放SE的代码
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', 'F', 'F'), chunkSize, chunkPos));
if (*reinterpret_cast<constDWORD *>(&dataPtr[chunkPos])
!= MAKEFOURCC('W', 'A','V', 'E')) ThrowIfFailed(E_FAIL);
从代码可以看出,CocosDenshion一开始就对mp3的文件进行了过滤,任何非wav的文件都会直接抛出异常。
如何解决这个问题呢?方法有二个。第一个最简单,就是修改资源本身。我们把mp3的格式转换为wav格式,网络上可以找到很多这样的工具。当然因为wav格式的文件体积比较大,所以这个方法不够完美。本文重点介绍第二个方法,也就是使用lamb库增加Windows
Phone 8平台对于mp3的支持。
Lamb是目前使用最广泛的MP3编码器,大家可以访问官网获取更多信息:
链接地址:
http://lame.sourceforge.net/download.php
使用Lamb所提供的解码模块,我们可以在WP8平台上实现mp3格式音乐的播放。我在这里借用一个网络上开源的代码:
链接地址:
https://github.com/tianjing/lame_wp8
下载源码后,我们需要用到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文件的增加头文件引用
#include"lame.h"
5. 在cocos2d\cocos\audio\wp8\mediaStreamer.cpp文件的增加Initialize_MP3()函数,用来初始化mp3文件。源码如下:
void MediaStreamer::Initialize_MP3(__inconstWCHAR* url)
{
#if 1
WCHAR filePath[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
wcscat_s(filePath, m_locationPath->Data());
// remove '/' or '\\'
wcscat_s(filePath,
(const WCHAR*)url[1]);
}
else
{
wcscat_s(filePath, m_locationPath->Data());
wcscat_s(filePath, url);
}
hip_t hip = hip_decode_init();
if (!hip)
{
printf("创建mp3解码失败");
return;
}
mp3data_struct mp3str;//mp3文件编码信息
std::vector<short*> mp3Buffer;//
mp3数据流
std::vector<int> mp3BufferSize;
int samples;
int mp3_bytes;
int write_bytes = 0;
const int BUF_SIZE =
512;
const int INBUF_SIZE =
4096;
const int MP3BUF_SIZE =
(int)(1.25 * BUF_SIZE) + 7200;
short pcm_l[INBUF_SIZE];
short pcm_r[INBUF_SIZE];
unsigned char mp3_buf[MP3BUF_SIZE];
FILE * MP3File;
std::wstring wstr = std::wstring(filePath);
std::string str_filePath = std::string(wstr.begin(), wstr.end());
auto error = fopen_s(&MP3File, str_filePath.c_str(), "rb");
mp3data_struct mp3Header;
while ((mp3_bytes = fread(mp3_buf,
1, 210, MP3File)) > 0)
{
samples = hip_decode_headers(hip, mp3_buf,
210, pcm_l, pcm_r, &mp3Header);
if (samples >
0)
{
short *tt = new short[samples*sizeof(short)];
memcpy((void*)tt,
(const void*)pcm_l, samples*sizeof(short));
mp3Buffer.push_back(tt);
write_bytes += samples*sizeof(short);
mp3BufferSize.push_back(samples*sizeof(short));
}
}
byte* _mp3Buffer = new byte[write_bytes];
byte* temp = _mp3Buffer;
int size = mp3BufferSize.size();
for (int i =
0; i < size; i++)
{
memcpy(temp, mp3Buffer[i], mp3BufferSize[i]);
delete mp3Buffer[i];
temp += mp3BufferSize[i];
}
mp3Buffer.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)
{
return;
}
std::string path(pszFilePath);
int sound = Hash(pszFilePath);
// no MP3 support for CC_PLATFORM_WP8
std::string::size_type pos = path.find(".mp3");
if (pos != path.npos)
{
mediaStreamer.Initialize_MP3(CCUtf8ToUnicode(pszFilePath).c_str());
}
else
{
mediaStreamer.Initialize(CCUtf8ToUnicode(pszFilePath).c_str());
}
至此大功告成,现在整个工程可以使用CocosDension顺利播放mp3音乐了。由此大家可以看到,这是一种非常好的方案,只需要做很少的代码修改,就实现了WP8平台对于mp3音乐的支持。推荐给大家的同时,也希望大家可以照此继续开发出对于ogg等更多音乐格式的支持的方案。关于这边博文,我借鉴了如下网站的内容。
链接地址:
/article/6972436.html
最后,我默认各位读者对于Cocos2dx已是非常的熟悉。如果大家对于Cocos2dx引擎本身或如何在WP8上建立Cocos2dx开发环境不清楚的话,推荐大家去观看我在微软虚拟在线课堂上的免费课程。
链接地址:
http://www.microsoftvirtualacademy.com/training-courses/854
谢谢!
Phone 8这个平台上,我希望通过自己的努力帮助大家解决移植过程中遇到的问题。
这一次我们来聊聊mp3音乐播放的问题。在游戏中播放音乐,大家一般使用Cocos2dx自带的CocosDenshion音乐引擎。使用方法相当简单,步骤如下:
工程中加入CocosDension引擎 (WP8的目录在cocos2d\cocos\audio\proj.wp8)
WinRT工程加入CocosDension的库引用
代码中加入必要的H文件和namespace
#include"SimpleAudioEngine.h"
using namespace CocosDenshion;
播放BGM的代码
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("xxx", true);
播放SE的代码
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', 'F', 'F'), chunkSize, chunkPos));
if (*reinterpret_cast<constDWORD *>(&dataPtr[chunkPos])
!= MAKEFOURCC('W', 'A','V', 'E')) ThrowIfFailed(E_FAIL);
从代码可以看出,CocosDenshion一开始就对mp3的文件进行了过滤,任何非wav的文件都会直接抛出异常。
如何解决这个问题呢?方法有二个。第一个最简单,就是修改资源本身。我们把mp3的格式转换为wav格式,网络上可以找到很多这样的工具。当然因为wav格式的文件体积比较大,所以这个方法不够完美。本文重点介绍第二个方法,也就是使用lamb库增加Windows
Phone 8平台对于mp3的支持。
Lamb是目前使用最广泛的MP3编码器,大家可以访问官网获取更多信息:
链接地址:
http://lame.sourceforge.net/download.php
使用Lamb所提供的解码模块,我们可以在WP8平台上实现mp3格式音乐的播放。我在这里借用一个网络上开源的代码:
链接地址:
https://github.com/tianjing/lame_wp8
下载源码后,我们需要用到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文件的增加头文件引用
#include"lame.h"
5. 在cocos2d\cocos\audio\wp8\mediaStreamer.cpp文件的增加Initialize_MP3()函数,用来初始化mp3文件。源码如下:
void MediaStreamer::Initialize_MP3(__inconstWCHAR* url)
{
#if 1
WCHAR filePath[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
wcscat_s(filePath, m_locationPath->Data());
// remove '/' or '\\'
wcscat_s(filePath,
(const WCHAR*)url[1]);
}
else
{
wcscat_s(filePath, m_locationPath->Data());
wcscat_s(filePath, url);
}
hip_t hip = hip_decode_init();
if (!hip)
{
printf("创建mp3解码失败");
return;
}
mp3data_struct mp3str;//mp3文件编码信息
std::vector<short*> mp3Buffer;//
mp3数据流
std::vector<int> mp3BufferSize;
int samples;
int mp3_bytes;
int write_bytes = 0;
const int BUF_SIZE =
512;
const int INBUF_SIZE =
4096;
const int MP3BUF_SIZE =
(int)(1.25 * BUF_SIZE) + 7200;
short pcm_l[INBUF_SIZE];
short pcm_r[INBUF_SIZE];
unsigned char mp3_buf[MP3BUF_SIZE];
FILE * MP3File;
std::wstring wstr = std::wstring(filePath);
std::string str_filePath = std::string(wstr.begin(), wstr.end());
auto error = fopen_s(&MP3File, str_filePath.c_str(), "rb");
mp3data_struct mp3Header;
while ((mp3_bytes = fread(mp3_buf,
1, 210, MP3File)) > 0)
{
samples = hip_decode_headers(hip, mp3_buf,
210, pcm_l, pcm_r, &mp3Header);
if (samples >
0)
{
short *tt = new short[samples*sizeof(short)];
memcpy((void*)tt,
(const void*)pcm_l, samples*sizeof(short));
mp3Buffer.push_back(tt);
write_bytes += samples*sizeof(short);
mp3BufferSize.push_back(samples*sizeof(short));
}
}
byte* _mp3Buffer = new byte[write_bytes];
byte* temp = _mp3Buffer;
int size = mp3BufferSize.size();
for (int i =
0; i < size; i++)
{
memcpy(temp, mp3Buffer[i], mp3BufferSize[i]);
delete mp3Buffer[i];
temp += mp3BufferSize[i];
}
mp3Buffer.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)
{
return;
}
std::string path(pszFilePath);
int sound = Hash(pszFilePath);
// no MP3 support for CC_PLATFORM_WP8
std::string::size_type pos = path.find(".mp3");
if (pos != path.npos)
{
mediaStreamer.Initialize_MP3(CCUtf8ToUnicode(pszFilePath).c_str());
}
else
{
mediaStreamer.Initialize(CCUtf8ToUnicode(pszFilePath).c_str());
}
至此大功告成,现在整个工程可以使用CocosDension顺利播放mp3音乐了。由此大家可以看到,这是一种非常好的方案,只需要做很少的代码修改,就实现了WP8平台对于mp3音乐的支持。推荐给大家的同时,也希望大家可以照此继续开发出对于ogg等更多音乐格式的支持的方案。关于这边博文,我借鉴了如下网站的内容。
链接地址:
/article/6972436.html
最后,我默认各位读者对于Cocos2dx已是非常的熟悉。如果大家对于Cocos2dx引擎本身或如何在WP8上建立Cocos2dx开发环境不清楚的话,推荐大家去观看我在微软虚拟在线课堂上的免费课程。
链接地址:
http://www.microsoftvirtualacademy.com/training-courses/854
谢谢!
相关文章推荐
- Unity 游戏移植到 Windows10 之路 -- 环境搭建
- 【Cocos2d-x for WP8 学习整理】(2)Cocos2d-Html5 游戏 《Fruit Attack》 WP8移植版 开源
- 【winows7+android-ndk-r9+Cygwin 】cocos2dx 2.*游戏移植Android平台完全手册
- 用cygwin移植c写的基于cocos2dx引擎的游戏为android编译时android不支持libiconv
- Cocos2dx3.10游戏移植到Windows平台遇到的坑总结
- Unity 游戏移植到 Windows10 之路 -- 环境搭建
- Cocos2d-x虚拟摇杆控制精灵上下左右运动----之游戏开发《赵云要格斗》(1) cocos2dx 3.3移植版
- Cocos2d-x项目移植到WP8系列之五:播放MP3
- 【COCOS2DX-游戏开发之二八】 Cocos2d-x-3c 设计之路 CocosBase CocosNet CocosWidget
- Cocos2d-x自定义按钮类控制精灵攻击----之游戏开发《赵云要格斗》(2) cocos2dx 3.3移植版
- cocos2dx wp8移植过程中的字体问题
- 将cocos2d-x游戏移植到win8/wp8上
- cocos 游戏移植到 Windows10 之路 -- 环境搭建
- Cocos2dx-3.2下wp8的移植
- cocos2dx- 游戏移植时的闪退问题
- [cocos2dx]怎样将Android手机游戏移植到电视?
- 1.cocos2dx记忆卡片游戏代码、并将游戏移植到“华为荣耀”手机上、移植中的问题总结
- [cocos2dx]如何将Android手机游戏移植到电视?
- 【Cocos2d-x for WP8 学习整理】(2)Cocos2d-Html5 游戏 《Fruit Attack》 WP8移植版 开源
- SDL游戏之路(二)--多平台支持--wp8