Qt音频相关类升级到Qt6之后的一些变化

Qt6 重构了多媒体模块,一些接口也随之变动,本文记录用到的部分音频相关类的变化。

(PS:Qt6.2 该模块才回归,模块名未变,依旧是 multimedia)

1.输入输出设备枚举

在 Qt5 中用 QAudioDeviceInfo 的静态函数来查询音频输入输出设备,一个实例表示一个设备:

//获取当前可用的音频输入/输出设备列表
//QAudio::AudioOutput 输出
//QAudio::AudioInput 输入
static QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode);
//默认音频输入设备
static QAudioDeviceInfo QAudioDeviceInfo::defaultInputDevice();
//默认音频输出设备
static QAudioDeviceInfo QAudioDeviceInfo::defaultOutputDevice();

Qt5 这个接口不能多个线程同时访问,否则程序会挂掉。而且 Qt5 没有提供输入输出设备变更的信号,得自己去轮询,或者捕获系统的设备插拔事件后去查询。在 Windows 下,因为使用了两个音频后端插件,所以设备名会重复两次,可以移除这个插件,或者根据支持的采样率等信息来过滤其中一个插件的结果,有一个插件只支持高采样。

在 Qt6 中,设备枚举由 QMediaDevices 进行,同时新增了输入输出变更的信号,设备信息由 QAudioDevice 实例表示:

//音频输入设备列表
static QList<QAudioDevice> QMediaDevices::audioInputs();
//音频输出设备列表
static QList<QAudioDevice> QMediaDevices::audioOutputs();
//默认音频输入设备
static QAudioDevice QMediaDevices::defaultAudioInput();
//默认音频输出设备
static QAudioDevice QMediaDevices::defaultAudioOutput();

//QMediaDevices 增加输入输出设备变更的信号
void audioInputsChanged();
void audioOutputsChanged();

Qt6 测试多线程获取设备信息没有崩溃。Qt6 Windows 只保留了一个音频后端插件,解决了之前的设备名重复的问题。

2.音频输入(录制)

Qt5 中通过输入设备 QAudioDeviceInfo 和音频格式 QAudioFormat 来构建一个 QAudioInput 进行录制,数据通过 QODevice 的读写接口回调输入:

QAudioInput(const QAudioDeviceInfo &audioDevice, 
            const QAudioFormat &format = QAudioFormat(), 
            QObject *parent = nullptr)

输入有推拉两种模式:

//pull
void QAudioInput::start(QIODevice *device); 
//push
QIODevice* QAudioInput::start(); 

如果是 pull 模式,则继承 QIOdevice,重写 writeData (外部写)接口用于接收数据。如果是 push 模式,关联 start() 返回的 QIODevice 的 readyRead 信号去取数据。这里借用示例的代码:

//切换推拉模式
void InputTest::toggleMode()
{
    m_audioInput->stop();
    toggleSuspend();

    // Change bewteen pull and push modes
    if (m_pullMode) {
        m_modeButton->setText(tr("Enable push mode"));
        m_audioInput->start(m_audioInfo.data());
    } else {
        m_modeButton->setText(tr("Enable pull mode"));
        auto io = m_audioInput->start();
        connect(io, &QIODevice::readyRead,
            [&, io]() {
                qint64 len = m_audioInput->bytesReady();
                const int BufferSize = 4096;
                if (len > BufferSize)
                    len = BufferSize;

                QByteArray buffer(len, 0);
                qint64 l = io->read(buffer.data(), len);
                if (l > 0)
                    m_audioInfo->write(buffer.constData(), l);
            });
    }

    m_pullMode = !m_pullMode;
}

Qt6 中该操作改为 QAudioSource 来完成,接口差不多,但是没有了 notify() 信号。  

3.音频输出(播放)

Qt5 中通过输出设备 QAudioDeviceInfo 和音频格式 QAudioFormat 来构建一个 QAudioOutput 进行播放,数据通过 QODevice 的读写接口回调输出:

QAudioOutput(const QAudioDeviceInfo &audioDevice, 
             const QAudioFormat &format = QAudioFormat(), 
             QObject *parent = nullptr)

输出有推拉两种模式:

//pull
void QAudioOutput::start(QIODevice *device); 
//push
QIODevice* QAudioOutput::start(); 

如果是 pull 模式,则继承 QIOdevice,重写 readData (外部读)接口用于输出数据。如果是 push 模式,用定时器给 start() 返回的 QIODevice 写数据。这里借用示例的代码:

//切换推拉模式
void AudioTest::toggleMode()
{
    m_pushTimer->stop();
    m_audioOutput->stop();
    toggleSuspendResume();

    if (m_pullMode) {
        //switch to pull mode (QAudioOutput pulls from Generator as needed)
        m_modeButton->setText(tr("Enable push mode"));
        m_audioOutput->start(m_generator.data());
    } else {
        //switch to push mode (periodically push to QAudioOutput using a timer)
        m_modeButton->setText(tr("Enable pull mode"));
        auto io = m_audioOutput->start();
        m_pushTimer->disconnect();

        connect(m_pushTimer, &QTimer::timeout, [this, io]() {
            if (m_audioOutput->state() == QAudio::StoppedState)
                return;

            QByteArray buffer(32768, 0);
            int chunks = m_audioOutput->bytesFree() / m_audioOutput->periodSize();
            while (chunks) {
               const qint64 len = m_generator->read(buffer.data(), m_audioOutput->periodSize());
               if (len)
                   io->write(buffer.data(), len);
               if (len != m_audioOutput->periodSize())
                   break;
               --chunks;
            }
        });

        m_pushTimer->start(20);
    }

    m_pullMode = !m_pullMode;
}

Qt6 中该操作改为 QAudioSink 来完成,接口差不多,但是没有了 notify() 信号。 

4.参考

Qt Creator 中搜索 audio 查询到的相关示例

Qt5 文档:https://doc.qt.io/qt-5/qtmultimedia-modules.html

Qt6 文档:https://doc.qt.io/qt-6/qtmultimedia-modules.html

猜你喜欢

转载自blog.csdn.net/gongjianbo1992/article/details/125649533