21 QAudioOutput放音的坑与解决方法

其实在写博文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);

你可能感兴趣的:(qt基础)