SDL播放音频工具类封装
一、概述
SDL2既可以播放视频也可以播放音频,前面介绍了视频的播放,本节对播放音频做一下总结。
步骤:
1.初始化
SDL_Init(SDL_INIT_AUDIO);
2.根据传入的参数打开音频设备
SDL_AudioSpec sdl_spec; sdl_spec.freq = audioSpec.freq; sdl_spec.format = audioSpec.format; sdl_spec.channels = audioSpec.channels; sdl_spec.samples = audioSpec.samples; sdl_spec.silence = 0; sdl_spec.userdata = this; sdl_spec.callback = AudioCallback; //打开SDL音频播放 if (SDL_OpenAudio(&sdl_spec, nullptr) < 0) { qDebug() << "SDL_OpenAudio->" << SDL_GetError() << endl; return false; }
3.开始/暂停播放
SDL_PauseAudio(0);//0代表开始播放,1代表停止播放
4.退出音频播放
SDL_QuitSubSystem(SDL_INIT_AUDIO);
5.设置音频播放的回调函数,并循环向音频缓冲区填充数据。声卡接收到数据后会自动播放
static void AudioCallback(void* userdata, unsigned char* stream, int len) { auto player = (YAudioPlayer*)userdata; player->Callback(stream, len); }
二、代码示例
工具类
1.YAudioPlayer.h
struct YAudioSpec { int freq = 44100;//音频采样率 unsigned short format = AUDIO_S16SYS;//采样格式(位深) unsigned char channels = 2;//声道数 unsigned short samples = 1024;//样本数 }; /// <summary> /// 音频数据结构体 /// </summary> struct YAudioData { std::vector<unsigned char> data; int offset = 0; //偏移位置 }; class YAudioPlayer { public: static YAudioPlayer* Create(); //打开音频,开始播放,并调用回调函数 virtual bool Open(YAudioSpec& audioSpec) = 0;//纯虚函数 virtual void Close() = 0; /// <summary> /// 需要向音频缓冲区塞数据,数据塞进去后声卡会自动播放,只要有数据就会播放 /// </summary> /// <param name="data"></param> /// <param name="size"></param> void Push(const unsigned char* data, int size) { std::unique_lock<std::mutex> lock(mux_); audio_datas_.push_back(YAudioData());//尾部插入数据 audio_datas_.back().data.assign(data, data + size);//获取队列尾部对象的引用,并分配音频数据 } //播放速度 virtual void SetSpeed(float s) { auto spec = audio_spec_; auto old_freq = spec.freq; spec.freq *= s; Open(spec); audio_spec_.freq = old_freq; } //音量 void set_volume(int v) { volume_ = v; } protected: YAudioPlayer(); ~YAudioPlayer(); /// <summary> /// SDL音频播放回调函数 /// </summary> /// <param name="stream">音频流缓冲区</param> /// <param name="len">数据长度</param> virtual void Callback(unsigned char* stream, int len) = 0; static void AudioCallback(void* userdata, unsigned char* stream, int len) { auto player = (YAudioPlayer*)userdata; player->Callback(stream, len); } std::list<YAudioData> audio_datas_;//音频缓冲列表 std::mutex mux_;//互斥锁 YAudioSpec audio_spec_; unsigned char volume_ = 128;// 0~128 音量 };
2.YAudioPlayer.cpp
class CYAudioPlayer : public YAudioPlayer { bool Open(YAudioSpec& audioSpec) { //退出上一次音频 SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_AudioSpec sdl_spec; sdl_spec.freq = audioSpec.freq; sdl_spec.format = audioSpec.format; sdl_spec.channels = audioSpec.channels; sdl_spec.samples = audioSpec.samples; sdl_spec.silence = 0; sdl_spec.userdata = this; sdl_spec.callback = AudioCallback; //打开SDL音频播放 if (SDL_OpenAudio(&sdl_spec, nullptr) < 0) { qDebug() << "SDL_OpenAudio->" << SDL_GetError() << endl; return false; } //开始播放 SDL_PauseAudio(0);//0代表开始播放,1代表停止播放 return true; } /// <summary> /// 关闭SDL音频播放器 /// </summary> void Close() { SDL_QuitSubSystem(SDL_INIT_AUDIO); std::unique_lock<std::mutex> lock(mux_); audio_datas_.clear(); } /// <summary> /// SDL音频播放回调函数 /// </summary> /// <param name="stream">指向 SDL 音频输出缓冲区的指针,需要填充音频数据</param> /// <param name="len">输出缓冲区的大小(字节数)</param> void Callback(unsigned char* stream, int len) { //初始化输出缓冲区,将其全部置零(静音状态),同时也会把残余旧数据清零,确保数据都是最新的 SDL_memset(stream, 0, len); //互斥锁,确保只有一个线程能够访问 std::unique_lock<std::mutex> lock(mux_); if (audio_datas_.empty())return; auto buf = audio_datas_.front(); // 1 buf 大于stream缓冲 offset记录位置 // 2 buf 小于stream 缓冲 拼接 int mixed_size = 0; //已经处理的字节数 int need_size = len; //还需要处理的字节数 while (mixed_size < len) { if (audio_datas_.empty())break; buf = audio_datas_.front(); //计算当前缓冲区剩余未处理的数据大小 int size = buf.data.size() - buf.offset; //若剩余数据超过需要的量,截取需要的部分 if (size > need_size) { size = need_size; } //使用SDL库将当前缓冲区数据混合到输出流中 SDL_MixAudio(stream + mixed_size, buf.data.data() + buf.offset, size, SDL_MIX_MAXVOLUME); //更新计数:减少需要的数据量,增加已处理的数据量 need_size -= size; mixed_size += size; //标记已处理数据的位置 buf.offset += size; //若当前缓冲区(list<YAudioData>)已全部处理完,从队列中移除 if (buf.offset >= buf.data.size()) { audio_datas_.pop_front(); } } } }; YAudioPlayer* YAudioPlayer::Create() { static CYAudioPlayer cyp; return &cyp; } YAudioPlayer::YAudioPlayer() { } YAudioPlayer::~YAudioPlayer() { //初始化SDL音频 SDL_Init(SDL_INIT_AUDIO); }
浙公网安备 33010602011771号