最新的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,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,_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],¤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 原文链接:https://www.f2er.com/cocos2dx/346559.html