Qt 播放pcm文件

简介

做了个简单的处理pcm数据的demo

主线程创建一个子线程,获取QAudioOutput一个周期所需要的数据量,循环的从文件中取出一个周期的数据量压入子线程的队列。

子线程创建后返回QAudioOutput一个周期所需要的数据量,开始等待主线程压入数据,当数据队列中有数据时将数据写入QAudioOutput缓冲区,当QAudioOutput对象内部缓冲区不足存放下一个数据包时,等待一毫秒后再处理。

bytesFree()返回内部缓冲区的空闲空间的字节数,每次写入所需的数据量periodSize(),填充满内部缓冲时暂停填入,以实现连续播放。

 

源码

PlayThread.h

/*!

* @breif 音频播放线程

*

* @author liujieda

* @create 2020年6月19日

*/

#include <queue>

#include <QThread>

#include <QMutex>

#include <QAudioOutput>

#include <QObject>

 

 

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<PCM_DATA *> _cacheData;

QMutex _mutex;

QAudioOutput * _output;

QIODevice * _io;

// 每个音频数据包的大小

int _perSize;

bool _isExit;

};

 

PlayThread.cpp

#include "PlayThread.h"

#include <QAudioFormat>

 

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 <QCoreApplication>

#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();

}

 

运行结果

 

Qt 播放pcm文件

整个工程文件已经上传,有兴趣可以下载来看下,点击下载