用QT使用SDL播放pcm文件
主要程序片断:
#include "playthread.h"
#include <QDebug>
#include <SDL2/SDL.h>
#include <QDebug>
#include <QFile>
#define FILENAME "../../haha/02_09_21_46_02.pcm"
#define SAMPLE_RATE 44100
#define SAMPLE_SIZE AUDIO_S16LSB
#define CHANNELS 2
#define BUFFER_SIZE 4096 //当缓存区小于音频缓存区时播放会出错,例如此处宏设为2048
PlayThread::PlayThread(QObject *parent):QThread(parent)
{
connect(this, &PlayThread::finished, this, &PlayThread::deleteLater); //线程结束后自动释放内存
}
PlayThread::~PlayThread()
{
disconnect();
requestInterruption();
quit();
wait();
qDebug()<< this <<"析构" << endl;
}
char *bufferData;
int bufferLen;
//等待音频设备回调(会回调多次);回调函数会在子线程(自己创建的),说明存在两个子线程
void pull_audio_data (void *userdata, Uint8 * stream, int len) //需要往stream中填充pcm数据;希望填充len这么大(samples*format*channels);本例中为4096个字节
{
qDebug() << "pull_audio_data" << len << endl; //len = samples*format*channels
//清空stream
SDL_memset(stream, 0,len);
//文件数据还没准备好
if(bufferLen <= 0) return;
//取bufferLen和len的最小值
len = (len > bufferLen) ? bufferLen : len; //主要是为了处理最后剩下的一点点数据,两个缓存区大小不一样时也需要判断(本例中是一样的)
//填充数据
SDL_MixAudio(stream, (const Uint8*)bufferData, len, SDL_MIX_MAXVOLUME);
bufferData += len; //指针向前移
bufferLen -= len;
}
void PlayThread::run()
{
//初始化Audio子系统
if(SDL_Init(SDL_INIT_AUDIO) < 0)
{
qDebug()<<"SDL_Init error" << SDL_GetError();
return;
}
//音频参数
SDL_AudioSpec spec;
//采样率
spec.freq = SAMPLE_RATE;
//通道数
spec.channels = CHANNELS;
//采样格式
spec.format = SAMPLE_SIZE;
//回调
spec.callback = pull_audio_data;
//音频缓冲区的样本数(这个值必须是2的幂)
spec.samples = 1024; //缓冲区实际字节数:1024X通道数2X采样格式16/8=4096个字节
//打开设备
if(SDL_OpenAudio(&spec, nullptr))
{
qDebug()<<"SDL_OpenAudio error" << SDL_GetError();
//清除所有子系统
SDL_Quit();
}
//打开文件
QFile file(FILENAME);
if(!file.open(QFile::ReadOnly))
{
qDebug() << "file open error" << endl;
SDL_CloseAudio();
//清除所有子系统
SDL_Quit();
}
//开始播放
SDL_PauseAudio(0);
//存放从文件中读取的数据
char data[BUFFER_SIZE];
while(!isInterruptionRequested())
{
bufferLen = file.read(data, BUFFER_SIZE);
if(bufferLen <=0) break;
bufferData = data;
while (bufferLen > 0)
{
//大于零,说明缓存区还有数据, 在此死循环,bufferLen会在子线程中被修改
}
}
file.close();
//关闭设备
SDL_CloseAudio();
//清除所有子系统
SDL_Quit();
}
代码中数据走向如下图所示:
但是此项目还存在一个问题,就是当数据缓存区小于音频缓存区时播放会出错,例如当数据大小宏设为2048时
[项目源码]项目名为sdl_test 链接:https://pan.baidu.com/s/1gMactd9alFY4PAhhAapY4Q?pwd=8888