最新的3.3cocos将openal也进行了引入,作为一个引擎使用者,当然是相当的开心!~ 那么我们首先来看一看~
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#ifndef __AUDIO_CACHE_H_
#define __AUDIO_CACHE_H_
#include <string>
#include <mutex>
#include <vector>
#include "CCPlatformMacros.h"
#include "AL/al.h"
#define QUEUEBUFFER_NUM 3
#define QUEUEBUFFER_TIME_STEP 0.1f
NS_CC_BEGIN
namespace experimental{
//前向声明
class AudioEngineImpl;
class AudioPlayer;
class CC_DLL AudioCache{
public:
//文件类型枚举
enum class FileFormat
{
UNKNOWN,//未知格式
OGG,// ogg格式音频,常用于android平台
MP3 // mp3格式音频,ios,android都较为常用
};
AudioCache();//构造函数
AudioCache(AudioCache&);//拷贝构造函数,值传递和用一个AudioCache去初始化另一个AudioCache的时候使用,尽量避免。
~AudioCache();//析构函数
void addCallbacks(const std::function<void()> &callback);//添加回调,参数为函数指针(形如void XXX())
protected:
void readDataTask(); //读数据任务
void invokingCallbacks();//请求回调
std::string _fileFullPath;
FileFormat _fileFormat;
//pcm data related stuff
size_t _pcmDataSize;
ALenum _alBufferFormat;//等于int32_t
int _channels;
ALuint _sampleRate; //等于uint32_t
size_t _bytesPerFrame;
float _duration;
/*Cache related stuff;
* Cache pcm data when sizeInBytes less than PCMDATA_CACHEMAXSIZE
*/
ALuint _alBufferId;//等于uint32_t
void* _pcmData;
size_t _bytesOfRead;
/*Queue buffer related stuff
* Streaming in OpenAL when sizeInBytes greater then PCMDATA_CACHEMAXSIZE
*/
char* _queBuffers[QUEUEBUFFER_NUM];
ALsizei _queBufferSize[QUEUEBUFFER_NUM];
int _queBufferFrames;
int _queBufferBytes;
bool _alBufferReady;
std::mutex _callbackMutex; //c11的互斥锁
std::vector< std::function<void()> > _callbacks;//vector存放函数指针
std::mutex _readDataTaskMutex;
int _mp3Encoding;
friend class AudioEngineImpl;//声明友元
friend class AudioPlayer;
} ;
}
NS_CC_END
#endif // __AUDIO_CACHE_H_
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include "AudioCache.h"
#include <thread>
#include <algorithm>
#include "base/CCConsole.h"
#include "mpg123.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisfile.h"
#include "base/ccUtils.h"
#define PCMDATA_CACHEMAXSIZE 2621440
using namespace cocos2d::experimental;
//列表初始化
AudioCache::AudioCache()
: _pcmData(nullptr)
, _pcmDataSize(0)
, _bytesOfRead(0)
, _alBufferReady(false)
, _fileFormat(FileFormat::UNKNOWN)
, _queBufferFrames(0)
, _queBufferBytes(0)
, _mp3Encoding(0)
{
//在这里其实就属于赋值了,不是初始化
}
//拷贝构造
AudioCache::AudioCache(AudioCache& cache)
{
_pcmData = cache._pcmData;
_pcmDataSize = cache._pcmDataSize;
_bytesOfRead = cache._bytesOfRead;
_alBufferReady = cache._alBufferReady;
_fileFormat = cache._fileFormat;
_queBufferFrames = cache._queBufferFrames;
_queBufferBytes = cache._queBufferBytes;
_mp3Encoding = cache._mp3Encoding;
}
AudioCache::~AudioCache()
{
if(_pcmData){
if (_alBufferReady){
alDeleteBuffers(1, &_alBufferId);//释放缓存ID
}
//wait for the 'readDataTask' task to exit
_readDataTaskMutex.lock();//上锁,等待读取数据任务离开
_readDataTaskMutex.unlock();//解锁
free(_pcmData);//释放指针,free接受参数为void*
}
if (_queBufferFrames > 0) {//有缓存帧数,则释放
for (int index = 0; index < QUEUEBUFFER_NUM; ++index) {
free(_queBuffers[index]);
}
}
}
void AudioCache::readDataTask()
{
_readDataTaskMutex.lock();//上任务锁
OggVorbis_File* vf = nullptr;//定义一个ogg数据结构体指针
mpg123_handle* mpg123handle = nullptr;//定义一个MP3数据的结构体指针
long totalFrames = 0;
switch (_fileFormat)
{
case FileFormat::OGG:
{
vf = new OggVorbis_File;//申请一块新的内存,并调用OggVorbis_File的构造,初始化,vf指向内存首地址
if (ov_fopen(_fileFullPath.c_str(), vf)){//类似于fopen,开始读取数据,将数据存放在vf所在的内存地址
log("Input does not appear to be an Ogg bitstream.\n");
goto ExitThread;//不喜欢goto!除非跳多重循环! 这里表示直接跳到ExitThread所在代码段
}
auto vi = ov_info(vf,-1);得到文件信息
totalFrames = (long)ov_pcm_total(vf,-1);//样本数
_bytesPerFrame = vi->channels * 2;//声道数*2 =每个字节帧
_alBufferFormat = (vi->channels > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;//缓存模式设置
_sampleRate = vi->rate;//采样率
_pcmDataSize = totalFrames * _bytesPerFrame; //扩展pcmdata的容量至指定大小
_duration = 1.0f * totalFrames / _sampleRate;//持续时间
}
break;
case FileFormat::MP3:
{
long rate = 0;
int error = MPG123_OK;
mpg123handle = mpg123_new(nullptr, &error);//初始化,并且因为传入引用,可从error,获取成功或者失败信息
if (!mpg123handle){
log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
goto ExitThread;//失败跳转,输出错误信息
}
if (mpg123_open(mpg123handle,_fileFullPath.c_str()) != MPG123_OK ||
mpg123_getformat(mpg123handle, &rate, &_channels, &_mp3Encoding) != MPG123_OK) {
log("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
goto ExitThread;
}//开始读取,并设置信息,如果失败,则跳转
if (_mp3Encoding == MPG123_ENC_SIGNED_16){
_bytesPerFrame = 2 * _channels;
}
else if (_mp3Encoding == MPG123_ENC_FLOAT_32){
_bytesPerFrame = 4 * _channels;
}
else{
log("Bad encoding: 0x%x!\n", _mp3Encoding);
goto ExitThread;
}//设置声道数量,根据不同的格式
_alBufferFormat = (_channels > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
_sampleRate = rate;//同上
/* Ensure that this output format will not change (it could, when we allow it). */
mpg123_format_none(mpg123handle);
mpg123_format(mpg123handle, rate, _channels, _mp3Encoding);
/* Ensure that we can get accurate length by call mpg123_length */
mpg123_scan(mpg123handle);//各个参数设置
auto framesLength = mpg123_length(mpg123handle);//帧长度
totalFrames = framesLength;
_pcmDataSize = totalFrames * _bytesPerFrame;
_duration = 1.0f * totalFrames / _sampleRate;//同上
}
break;
case FileFormat::UNKNOWN:
default:
break;
}
if (_pcmDataSize <= PCMDATA_CACHEMAXSIZE)
{
_pcmData = malloc(_pcmDataSize);//不超过限制内存长度下,申请内存
auto alError = alGetError();//获得错误信息
alGenBuffers(1, &_alBufferId);//申请一个al缓存
alError = alGetError();//得到错误信息
if (alError != AL_NO_ERROR) {
log("%s: attaching audio to buffer fail: %x\n", __FUNCTION__, alError);
goto ExitThread;
}
switch (_fileFormat)
{
case FileFormat::OGG:
{
int current_section;
unsigned int currPos = 0;
long readRet = 0;
do
{
readRet = ov_read(vf,(char*)_pcmData + _bytesOfRead,4096,0,2,1,¤t_section);//进行解码
if (readRet > 0){//readRet可能小于等于0,表示文件流中发生不明错误
_bytesOfRead += readRet;
}
} while (_bytesOfRead < _pcmDataSize);//确保有读取到数据,有疑问!如果异常,陷入死循环?
_alBufferReady = true;
_bytesOfRead = _pcmDataSize;
break;
}
case FileFormat::MP3:
{
size_t done = 0;
auto err = mpg123_read(mpg123handle,(unsigned char*)_pcmData, _pcmDataSize,&done);//mp3解码
if (err == MPG123_ERR){
log("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
goto ExitThread;
}
if (err == MPG123_DONE || err == MPG123_OK){//错误处理
_alBufferReady = true;
_pcmDataSize = done;
_bytesOfRead = done;
}
}
break;
case FileFormat::UNKNOWN:
default:
break;
}
alBufferData(_alBufferId,_alBufferFormat,_pcmData,_pcmDataSize,_sampleRate);//将数据放入缓存
}
else{//数据量很大时
_queBufferFrames = _sampleRate * QUEUEBUFFER_TIME_STEP;
_queBufferBytes = _queBufferFrames * _bytesPerFrame;
for (int index = 0; index < QUEUEBUFFER_NUM; ++index) {
_queBuffers[index] = (char*)malloc(_queBufferBytes);//3段申请内存
switch (_fileFormat){
case FileFormat::MP3:
{
size_t done = 0;
mpg123_read(mpg123handle,(unsigned char*)_queBuffers[index], _queBufferBytes,&done);//解码
_queBufferSize[index] = done;
_bytesOfRead += done;
}
break;
case FileFormat::OGG:
{
int current_section;
auto readRet = ov_read(vf,_queBuffers[index],_queBufferBytes,0,2,1,¤t_section);//解码
_queBufferSize[index] = readRet;
}
break;
}
}
}
//goto的目的代码段
ExitThread:
switch (_fileFormat)
{
case FileFormat::OGG:
ov_clear(vf);//字节流清空,该函数中已调用了fclose函数
delete vf;
break;
case FileFormat::MP3:
mpg123_close(mpg123handle);
mpg123_delete(mpg123handle);//关闭释放内存,数据都已存放到缓存中
break;
case FileFormat::UNKNOWN:
default:
break;
}
_readDataTaskMutex.unlock();//读取数据完毕,解锁
if (_queBufferFrames > 0)
_alBufferReady = true;
invokingCallbacks();
}
void AudioCache::invokingCallbacks()
{
_callbackMutex.lock();//回调处理上锁
auto count = _callbacks.size();
for (size_t index = 0; index < count; ++index) {
_callbacks[index]();//依次执行回调函数
}
_callbacks.clear();
_callbackMutex.unlock();//回调处理解锁
}
void AudioCache::addCallbacks(const std::function<void ()> &callback)
{
_callbackMutex.lock();//上锁
if (_alBufferReady) {//当数据已经在缓存中,直接执行回调
callback();
} else {
_callbacks.push_back(callback);//否则,等待执行
}
_callbackMutex.unlock();//解锁
}
#endif
鉴于时间,不便多说,求指点!~~ T T