一.QT6音频调用及与QT5的区别

1.音频输入

QAudioSource代替QAudioInput类

QAudioSource类提供了一个接口,用于从音频输入设备接收音频数据。

Header: #include <QAudioSource>

qmake: QT += multimedia

2.音频输出

QAudioSink代替QAudioOutput类

QAudioSink类提供了一个接口,用于将音频数据发送到音频输出设备。

Header: #include <QAudioSink>

qmake: QT += multimedia

二.代码示例

其功能为本地产生一些声音数据,然后输出到扬声器或者耳机。

可以应用在通过网络接收的声音数据,然后输出到音频播放设备;

代码为纯qt实现,可以应用在windows、linux和android上,无需修改。

1.audiooutput.h

#ifndef AUDIOOUTPUT_H

#define AUDIOOUTPUT_H

#include <QAudioSink>

#include <QByteArray>

#include <QComboBox>

#include <QIODevice>

#include <QLabel>

#include <QMainWindow>

#include <QMediaDevices>

#include <QObject>

#include <QPushButton>

#include <QScopedPointer>

#include <QSlider>

#include <QTimer>

#include <math.h>

class Generator : public QIODevice

{

    Q_OBJECT

public:

    Generator(const QAudioFormat &format, qint64 durationUs, int sampleRate);

    void start();

    void stop();

    qint64 readData(char *data, qint64 maxlen) override;

    qint64 writeData(const char *data, qint64 len) override;

    qint64 bytesAvailable() const override;

    qint64 size() const override { return m_buffer.size(); }

private:

    void generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate);

private:

    qint64 m_pos = 0;

    QByteArray m_buffer;

};

class AudioTest : public QMainWindow

{

    Q_OBJECT

public:

    AudioTest();

    ~AudioTest();

private:

    void initializeWindow();

    void initializeAudio(const QAudioDevice &deviceInfo);

private:

    QMediaDevices *m_devices = nullptr;

    QTimer *m_pushTimer = nullptr;

    // Owned by layout

    QPushButton *m_modeButton = nullptr;

    QPushButton *m_suspendResumeButton = nullptr;

    QComboBox *m_deviceBox = nullptr;

    QLabel *m_volumeLabel = nullptr;

    QSlider *m_volumeSlider = nullptr;

    QScopedPointer<Generator> m_generator;

    QScopedPointer<QAudioSink> m_audioOutput;

    bool m_pullMode = true;

private slots:

    void toggleMode();

    void toggleSuspendResume();

    void deviceChanged(int index);

    void volumeChanged(int);

    void updateAudioDevices();

};

#endif // AUDIOOUTPUT_H

2.audiooutput.cpp

#include "audiooutput.h"

#include <QAudioDevice>

#include <QAudioSink>

#include <QDebug>

#include <QVBoxLayout>

#include <QtEndian>

#include <QtMath>

Generator::Generator(const QAudioFormat &format, qint64 durationUs, int sampleRate)

{

    if (format.isValid())

        generateData(format, durationUs, sampleRate);

}

void Generator::start()

{

    open(QIODevice::ReadOnly);

}

void Generator::stop()

{

    m_pos = 0;

    close();

}

void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate)

{

    const int channelBytes = format.bytesPerSample();

    const int sampleBytes = format.channelCount() * channelBytes;

    qint64 length = format.bytesForDuration(durationUs);

    Q_ASSERT(length % sampleBytes == 0);

    Q_UNUSED(sampleBytes); // suppress warning in release builds

    m_buffer.resize(length);

    unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data());

    int sampleIndex = 0;

    while (length) {

        // Produces value (-1..1)

        const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex++ % format.sampleRate())

                             / format.sampleRate());

        for (int i = 0; i < format.channelCount(); ++i) {

            switch (format.sampleFormat()) {

            case QAudioFormat::UInt8:

                *reinterpret_cast<quint8 *>(ptr) = static_cast<quint8>((1.0 + x) / 2 * 255);

                break;

            case QAudioFormat::Int16:

                *reinterpret_cast<qint16 *>(ptr) = static_cast<qint16>(x * 32767);

                break;

            case QAudioFormat::Int32:

                *reinterpret_cast<qint32 *>(ptr) =

                        static_cast<qint32>(x * std::numeric_limits<qint32>::max());

                break;

            case QAudioFormat::Float:

                *reinterpret_cast<float *>(ptr) = x;

                break;

            default:

                break;

            }

            ptr += channelBytes;

            length -= channelBytes;

        }

    }

}

qint64 Generator::readData(char *data, qint64 len)

{

    qint64 total = 0;

    if (!m_buffer.isEmpty()) {

        while (len - total > 0) {

            const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total);

            memcpy(data + total, m_buffer.constData() + m_pos, chunk);

            m_pos = (m_pos + chunk) % m_buffer.size();

            total += chunk;

        }

    }

    return total;

}

qint64 Generator::writeData(const char *data, qint64 len)

{

    Q_UNUSED(data);

    Q_UNUSED(len);

    return 0;

}

qint64 Generator::bytesAvailable() const

{

    return m_buffer.size() + QIODevice::bytesAvailable();

}

AudioTest::AudioTest() : m_devices(new QMediaDevices(this)), m_pushTimer(new QTimer(this))

{

    initializeWindow();

    initializeAudio(m_devices->defaultAudioOutput());

    qDebug()<<"11111111111111111111";

}

AudioTest::~AudioTest()

{

    m_pushTimer->stop();

}

void AudioTest::initializeWindow()

{

    QWidget *window = new QWidget;

    QVBoxLayout *layout = new QVBoxLayout;

    m_deviceBox = new QComboBox(this);

    const QAudioDevice &defaultDeviceInfo = m_devices->defaultAudioOutput();

    m_deviceBox->addItem(defaultDeviceInfo.description(), QVariant::fromValue(defaultDeviceInfo));

    for (auto &deviceInfo : m_devices->audioOutputs()) {

        if (deviceInfo != defaultDeviceInfo)

            m_deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));

    }

    connect(m_deviceBox, QOverload<int>::of(&QComboBox::activated), this,

            &AudioTest::deviceChanged);

    connect(m_devices, &QMediaDevices::audioOutputsChanged, this, &AudioTest::updateAudioDevices);

    layout->addWidget(m_deviceBox);

    m_modeButton = new QPushButton(this);

    connect(m_modeButton, &QPushButton::clicked, this, &AudioTest::toggleMode);

    layout->addWidget(m_modeButton);

    m_suspendResumeButton = new QPushButton(this);

    connect(m_suspendResumeButton, &QPushButton::clicked, this, &AudioTest::toggleSuspendResume);

    layout->addWidget(m_suspendResumeButton);

    QHBoxLayout *volumeBox = new QHBoxLayout;

    m_volumeLabel = new QLabel;

    m_volumeLabel->setText(tr("Volume:"));

    m_volumeSlider = new QSlider(Qt::Horizontal);

    m_volumeSlider->setMinimum(0);

    m_volumeSlider->setMaximum(100);

    m_volumeSlider->setSingleStep(10);

    connect(m_volumeSlider, &QSlider::valueChanged, this, &AudioTest::volumeChanged);

    volumeBox->addWidget(m_volumeLabel);

    volumeBox->addWidget(m_volumeSlider);

    layout->addLayout(volumeBox);

    window->setLayout(layout);

    setCentralWidget(window);

    window->show();

}

void AudioTest::initializeAudio(const QAudioDevice &deviceInfo)

{

    QAudioFormat format = deviceInfo.preferredFormat();

    const int durationSeconds = 1;

    const int toneSampleRateHz = 600;

    m_generator.reset(new Generator(format, durationSeconds * 1000000, toneSampleRateHz));

    m_audioOutput.reset(new QAudioSink(deviceInfo, format));

    m_generator->start();

    qreal initialVolume = QAudio::convertVolume(m_audioOutput->volume(), QAudio::LinearVolumeScale,

                                                QAudio::LogarithmicVolumeScale);

    m_volumeSlider->setValue(qRound(initialVolume * 100));

    toggleMode();

}

void AudioTest::deviceChanged(int index)

{

    m_generator->stop();

    m_audioOutput->stop();

    m_audioOutput->disconnect(this);

    initializeAudio(m_deviceBox->itemData(index).value<QAudioDevice>());

}

void AudioTest::volumeChanged(int value)

{

    qreal linearVolume = QAudio::convertVolume(value / qreal(100), QAudio::LogarithmicVolumeScale,

                                               QAudio::LinearVolumeScale);

    m_audioOutput->setVolume(linearVolume);

}

void AudioTest::updateAudioDevices()

{

    m_deviceBox->clear();

    const QList<QAudioDevice> devices = m_devices->audioOutputs();

    for (const QAudioDevice &deviceInfo : devices)

        m_deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));

}

void AudioTest::toggleMode()

{

    m_pushTimer->stop();

    m_audioOutput->stop();

    toggleSuspendResume();

    if (m_pullMode) {

        // switch to pull mode (QAudioSink 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 QAudioSink 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;

            int len = m_audioOutput->bytesFree();

            QByteArray buffer(len, 0);

            len = m_generator->read(buffer.data(), len);

            if (len)

                io->write(buffer.data(), len);

        });

        m_pushTimer->start(10);

    }

    m_pullMode = !m_pullMode;

}

void AudioTest::toggleSuspendResume()

{

    if (m_audioOutput->state() == QAudio::SuspendedState

        || m_audioOutput->state() == QAudio::StoppedState) {

        m_audioOutput->resume();

        m_suspendResumeButton->setText(tr("Suspend playback"));

    } else if (m_audioOutput->state() == QAudio::ActiveState) {

        m_audioOutput->suspend();

        m_suspendResumeButton->setText(tr("Resume playback"));

    } else if (m_audioOutput->state() == QAudio::IdleState) {

        // no-op

    }

}

3.测试页面

可以选择输出设备,音量调节,停止和继续。

代码下载地址:https://download.csdn.net/download/xieliru/89050304

QT6实现音频输出方法-LMLPHP

03-30 08:49