世界杯预选赛亚洲区_高达世界杯 - fzxzyy.com

21 QAudioOutput放音的坑与解决方法

- 19世界杯

其实在写博文http://blog.csdn.net/jklinux/article/details/72355485时,并没有测试放音,原以为就是一件很容易的事. 为了后期写音视频播放器时可以在QT用QAudioOutput放音(基本上所有案例都是调用SDL放音), 确定可行性。实实在在测试了一回,真的发现了问题。

QAudioOutput *aoutput = new QAudioOutput(format); //创建QAudioOutput对象并初始化后

QIODevice *dev = aoutput->start(); //调用start函数后, 返回QIODevice对象的地址.

然后就可以调用dev->write(...)函数进行放音。在播放器里应是边解码边把解码得到的pcm数据存入在内存数组里,再通过dev->write函数提交给声卡发声, 但声卡里的数据缓冲区大小肯定是有限制,不可能一味的write就行。 正常情况下应是写入声音数据后,等到有些数据完成播放后再接着写入部分数据。

QIODevice对象有信号bytesWritten(qint64), 理论上可用个槽函数连接此信号即可得知多少数据已完成播放,再写入等量的声音数据。

但连接此信号的槽函数根本就没有得到触发(不知道这是不是它的bug).

接着再试在线程里搞死循环: dev->write(...)然后dev->waitForBytesWritten(..) 一样无法解决

后来通过QT里带的"Audio Output Example"发现的解决方法。

它里面用的是void QAudioOutput::start(QIODevice *device)来启动, 而当声卡需要数据来播放时会自动调用device->readData(..)函数.

并且readData是个虚函数,也就是可以通过继承QIODevice,重新实现readData函数。这样当声卡需要数据时,就可以取到我们自己备好的数据了。

实现代码:

mydevice.h

#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

mydevice.cpp

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

}

qint64 MyDevice::readData(char *data, qint64 maxlen) // data为声卡的数据缓冲区地址, 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)

{

}

调用代码:

QAudioFormat fmt;

fmt.setSampleRate(8000);

fmt.setChannelCount(1);

fmt.setSampleSize(8);

fmt.setCodec("audio/pcm");

out = new QAudioOutput(fmt, this); //创建声音输出对象并初始化

//先把文件的pcm数据弄到内存数组里

QByteArray ba;

QFile f("/my.raw"); // my.raw是用arecod录制的

if (!f.open(QIODevice::ReadOnly))

exit(0);

ba = f.readAll();

f.close();

MyDevice *dev = new MyDevice(ba); //创建自定义的IO设备

out->start(dev);

沙特国家队最新大名单2022世界杯核心主力阵容一览
石家庄妙指仙境初谈