最新的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.1fNS_CC_BEGINnamespace 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 2621440using 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 总结
以上是内存溢出为你收集整理的菜鸟也能学cocos2dx 3.3 AudioCache全部内容,希望文章能够帮你解决菜鸟也能学cocos2dx 3.3 AudioCache所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)