简介
做了个简单的处理pcm数据的demo
主线程创建一个子线程,获取QAudioOutput一个周期所需要的数据量,循环的从文件中取出一个周期的数据量压入子线程的队列。
子线程创建后返回QAudioOutput一个周期所需要的数据量,开始等待主线程压入数据,当数据队列中有数据时将数据写入QAudioOutput缓冲区,当QAudioOutput对象内部缓冲区不足存放下一个数据包时,等待一毫秒后再处理。
bytesFree()返回内部缓冲区的空闲空间的字节数,每次写入所需的数据量periodSize(),填充满内部缓冲时暂停填入,以实现连续播放。
源码
PlayThread.h
/*!
* @breif 音频播放线程
*
* @author liujieda
* @create 2020年6月19日
*/
#include
#include
#include
#include
#include
typedef struct _pcm_data {
char * data;
int size;
}PCM_DATA;
class PlayThread : public QThread {
public:
PlayThread(QThread* parent = 0);
~PlayThread();
void addData(PCM_DATA * data);
int startThread(int simpleRate, int simpleSize, int channel);
void setExit(bool isExit = true);
protected:
void virtual run();
private:
std::queue
QMutex _mutex;
QAudioOutput * _output;
QIODevice * _io;
// 每个音频数据包的大小
int _perSize;
bool _isExit;
};
PlayThread.cpp
#include "PlayThread.h"
#include
PlayThread::PlayThread(QThread* parent /* = 0 */) : QThread(parent) {
_output = NULL;
_io = NULL;
_isExit = false;
}
PlayThread::~PlayThread() {
if (_output) {
delete _output;
_output = NULL;
}
}
/*
* @brief 像数据队列中压入数据
*/
void PlayThread::addData(PCM_DATA * data) {
_mutex.lock();
_cacheData.push(data);
_mutex.unlock();
}
/*
* @brief 初始化音频输出格式,开始等待处理音频数据
* 返回一个周期所必需要的数据量
*
* @param int simpleRate 音频数据采样率
* @param int simpleSize 样本大小,也就是振幅
* @param int channel 声道序号
*/
int PlayThread::startThread(int simpleRate, int simpleSize, int channel) {
QAudioFormat fmt;
fmt.setSampleRate(simpleRate);
fmt.setSampleSize(simpleSize);
fmt.setChannelCount(channel);
fmt.setCodec("audio/pcm");
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setSampleType(QAudioFormat::UnSignedInt);
_output = new QAudioOutput(fmt);
_io = _output->start();
_perSize = _output->periodSize();
start();
return _perSize;
}
/*
* @brief 线程入口
* 循环检测音频数据队列是否为空,不为空时开始处理数据
* 当QAudioOutput对象内部缓冲区不足存放下一个数据包时,等待一毫秒后再处理
* bytesFree()返回内部缓冲区的空闲空间的字节数
* 每次写入所需的数据量periodSize(),填充满内部缓冲时暂停填入,以实现连续播放
*/
void PlayThread::run() {
while (true) {
_mutex.lock();
if (_isExit) {
break;
}
if (_cacheData.size() > 0) {
int freeSize = _output->bytesFree();
printf("free size:%d,input size:%d\n", freeSize, _perSize);
if (freeSize < _perSize) {
msleep(1);
_mutex.unlock();
continue;
}
PCM_DATA * data = _cacheData.front();
_cacheData.pop();
if (data->size > 0) {
_io->write(data->data, data->size);
}
delete []data->data;
delete data;
}
_mutex.unlock();
}
}
/*
* @brief 安全退出线程
*
* @param bool isExit 默认true,停止循环任务
*/
void PlayThread::setExit(bool isExit) {
_mutex.lock();
_isExit = isExit;
_mutex.unlock();
}
main.cpp
/*!
* @breif 测试
*
* @author liujieda
* @create 2020年6月19日
*/
#include
#include "PlayThread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
FILE *fp = fopen("16k.pcm", "rb");
if (fp) {
PlayThread * playThread = new PlayThread;
int size = playThread->startThread(16000, 16, 1);
// 循环读取音频数据压入队列
while (!feof(fp)) {
PCM_DATA * data = new PCM_DATA;
data->data = new char[size];
int len = fread(data->data, 1, size, fp);
if (len <= 0) {
break;
}
data->size = len;
playThread->addData(data);
}
fclose(fp);
playThread->wait();
playThread->setExit();
delete playThread;
} else {
printf("open file failed!\n");
}
return a.exec();
}
运行结果
整个工程文件已经上传,有兴趣可以下载来看下,点击下载。