菜鸟也能学cocos2dx 3.3 AudioCache

最新的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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值