QT6实现音频输出方法

一.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

相关推荐
直裾2 分钟前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城23 分钟前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
_Shirley36 分钟前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
Fre丸子_1 小时前
ffmpeg之播放一个yuv视频
ffmpeg·音视频
9527华安1 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
码银2 小时前
【python】银行客户流失预测预处理部分,独热编码·标签编码·数据离散化处理·数据筛选·数据分割
开发语言·python
从善若水2 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
hedalei2 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng2 小时前
安卓多渠道apk配置不同签名
android
catmes2 小时前
设置浏览器声音或视频的自动播放策略
chrome·音视频·edge浏览器