Qt播放PCM音频(裸流)文件的两种方法

网友投稿 2359 2022-09-28 08:20:07

Qt播放PCM音频(裸流)文件的两种方法

QAudioOutput实现的,先用QFile打开PCM文件,然后进行音频参数设置,最后调用QAudioOutput的start函数进行播放;方法二也用到了QAudioOutput,先将PCM文件读取到内存,然后进行音频参数设置,最后从内存中定时读取部分数据,调用QIODevice的write函数将这部分数据写入到扬声器。与方法一相比,方法二虽然麻烦,但是这种方法是进行语音编解码和语音网络传输的基础。

方法一:

#include #include #include #include int main(int argc, char *argv[]){ QApplication a(argc, argv); QFile inputFile; inputFile.setFileName("test.pcm"); inputFile.open(QIODevice::ReadOnly); //设置采样格式 QAudioFormat audioFormat; //设置采样率 audioFormat.setSampleRate(44100); //设置通道数 audioFormat.setChannelCount(2); //设置采样大小,一般为8位或16位 audioFormat.setSampleSize(16); //设置编码方式 audioFormat.setCodec("audio/pcm"); //设置字节序 audioFormat.setByteOrder(QAudioFormat::LittleEndian); //设置样本数据类型 audioFormat.setSampleType(QAudioFormat::UnSignedInt); QAudioOutput *audio = new QAudioOutput( audioFormat, 0); audio->start(&inputFile); return a.exec();}

方法二:

#ifndef TESTAUDIO_H#define TESTAUDIO_H#include #include #include #include #include class TestAudio : public QWidget{ Q_OBJECTpublic: explicit TestAudio(QWidget *parent = nullptr); ~TestAudio();private: int fileLength; char *fileBuffer; QAudioOutput *audioOutput; QIODevice *streamOut; QTimer *timer; QByteArray tempBuffer;private slots: void slotTimeout();};#endif // TESTAUDIO_H

#include "TestAudio.h"#include #include #include #include TestAudio::TestAudio(QWidget *parent) : QWidget(parent){ //获取文件的指针. FILE *file=fopen(QString(qApp->applicationDirPath()+"/NorwayForest-500.pcm").toStdString().c_str(),"rb"); if(!file) { qDebug()<<"Open Failed"; return; } //把指针移动到文件的结尾 ,获取文件长度. fseek(file,0,SEEK_END); //获取文件长度. fileLength=ftell(file); //定义数组长度. fileBuffer=new char[fileLength+1]; //把指针移动到文件开头 因为我们一开始把指针移动到结尾,如果不移动回来 会出错. rewind(file); //读文件. fread(fileBuffer,1,fileLength,file); //把读到的文件最后一位 写为0 要不然系统会一直寻找到0后才结束. fileBuffer[fileLength]=0; //关闭文件. fclose(file); qDebug()<start(); //定时器. timer=new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(slotTimeout())); timer->start(10);}TestAudio::~TestAudio(){}void TestAudio::slotTimeout(){ static int i=0; if(istate()!=QAudio::StoppedState&& audioOutput->state()!=QAudio::SuspendedState) { int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); while (chunks) { if (tempBuffer.length() >= audioOutput->periodSize()) { //写入到扬声器. streamOut->write(tempBuffer.data(),audioOutput->periodSize()); tempBuffer = tempBuffer.mid(audioOutput->periodSize()); } else { //写入到扬声器. streamOut->write(tempBuffer); tempBuffer.clear(); break; } --chunks; } } } i++;}

PCM裸流1秒的数据量是44100×16×2/8=176400字节,那么10毫秒的数据量就是1764字节,所以定时器定时10毫秒,每隔10毫秒从数组中顺序读取1746个字节。

不幸的是,方法二会有杂音,感谢评论区winwingy的提醒。方法三如下:

方法三

#ifndef MYDEVICE_H#define MYDEVICE_H#include class MyDevice : public QIODevice{private: QByteArray data_pcm; //存放pcm数据 int len_written; //记录已写入多少字节public: MyDevice(QByteArray pcm); //创建对象传递pcm数据 ~MyDevice(); qint64 readData(char *data, qint64 maxlen); //重新实现的虚函数 qint64 writeData(const char *data, qint64 len); //它是个纯虚函数, 不得不实现};#endif // MYDEVICE_H

#include "MyDevice.h"#include MyDevice::MyDevice(QByteArray pcm) : data_pcm(pcm){ this->open(QIODevice::ReadOnly); // 为了解决QIODevice::read (QIODevice): device not open. len_written = 0;}MyDevice::~MyDevice(){ this->close();}// data为声卡的数据缓冲区地址, maxlen为声卡缓冲区最大能存放的字节数.qint64 MyDevice::readData(char *data, qint64 maxlen){ if (len_written >= data_pcm.size()) return 0; int len; //计算未播放的数据的长度. len = (len_written+maxlen) > data_pcm.size() ? (data_pcm.size() - len_written) : maxlen; memcpy(data, data_pcm.data()+len_written, len); //把要播放的pcm数据存入声卡缓冲区里. len_written += len; //更新已播放的数据长度. return len;}qint64 MyDevice::writeData(const char *data, qint64 len){ return len;}

#ifndef TESTAUDIO_H#define TESTAUDIO_H#include class TestAudio : public QWidget{ Q_OBJECTpublic: explicit TestAudio(QWidget *parent = nullptr); ~TestAudio();};#endif // TESTAUDIO_H

#include "TestAudio.h"#include "MyDevice.h"#include #include #include #include #include TestAudio::TestAudio(QWidget *parent) : QWidget(parent){ QAudioFormat fmt; fmt.setSampleRate(44100); fmt.setChannelCount(2); fmt.setSampleSize(16); fmt.setCodec("audio/pcm"); fmt.setCodec("audio/pcm"); fmt.setByteOrder(QAudioFormat::LittleEndian); fmt.setSampleType(QAudioFormat::UnSignedInt); QAudioOutput *out = new QAudioOutput(fmt, this); QByteArray ba; QFile f(qApp->applicationDirPath()+"/NorwayForest-500.pcm"); if (!f.open(QIODevice::ReadOnly)) exit(0); ba = f.readAll(); f.close(); MyDevice *dev = new MyDevice(ba); out->start(dev);}TestAudio::~TestAudio(){}

方法三虽然是播放的本地文件,如果要播放网络流的话,实时给QByteArray填充数据就行。

更详细的可参考Qt自带例子Audio Output:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:/usr/bin/ld:cannot find -lIN
下一篇:Qt OpenGL:学习现代3D图形编程之二,玩转色彩
相关文章