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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/576724.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang实战:深入hash/crc64标准库的应用与技巧

Golang实战&#xff1a;深入hash/crc64标准库的应用与技巧 引言hash/crc64简介基本原理核心功能 环境准备安装Golang创建一个新的Golang项目引入hash/crc64包测试环境配置 hash/crc64的基本使用计算字符串的CRC64校验和计算文件的CRC64校验和 高级技巧与应用数据流和分块处理网…

AC-DC高压线性恒流驱动IC芯片SM2082EGS明微LED球泡灯应用方案

AC-DC高压线性恒流驱动IC芯片是一种专门用于驱动LED灯珠的高功率线性芯片。它采用了无电解电容器、无变压器、电感器的直流驱动电源设计&#xff0c;使得高压线性恒流电源可以直接集成在LED光源板上&#xff0c;组成所谓的“光电引擎”。这样的设计不仅大大节省了人工成本&…

有效的数独-java

题目描述: 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#…

在Three.js中,EXRExporter API通常用于将场景导出为EXR(OpenEXR)格式的图像。

demo案例 在Three.js中&#xff0c;EXRExporter API通常用于将场景导出为EXR&#xff08;OpenEXR&#xff09;格式的图像。下面是对其入参、出参、方法和属性的详细解释&#xff1a; 入参&#xff08;输入参数&#xff09;&#xff1a; scene&#xff08;场景&#xff09;&…

高效物联网连接技术创新:ECWAN边缘协同自组网的未来——基于ChirpLAN窄带扩频技术的无线混合组网

物联网是指将各种物理设备通过互联网进行连接和通信的技术。它是一个庞大的网络&#xff0c;由传感器、设备、网络和云服务组成&#xff0c;旨在实现对物体的远程监测、控制和数据采集。 基于ChirpLAN窄带扩频技术的无线混合组网协议ChirpLAN&#xff0c;ChirpLAN是基于其自有的…

【超图 SuperMap3D】【基础API使用示例】51、超图SuperMap3D - 绘制圆|椭圆形面标注并将视角定位过去

前言 引擎下载地址&#xff1a;[添加链接描述](http://support.supermap.com.cn/DownloadCenter/DownloadPage.aspx?id2524) 绘制圆形或者椭圆形效果 核心代码 entity viewer.entities.add({// 圆中心点position: { x: -1405746.5243351874, y: 4988274.8462937465, z: 370…

岭师大数据技术原理与应用-序章-软工版

HeZaoCha-CSDN博客 序章—软工版 一、环境介绍1. VMware Workstation Pro2. CentOS3. Java4. Hadoop5. HBase6. MySQL7. Hive 二、系统安装1. 虚拟网络编辑器2. 操作系统安装 三、结尾 先说说哥们写这系列博客的原因&#xff0c;本来学完咱也没想着再管部署这部分问题的说&…

Jackson 2.x 系列【6】注解大全篇二

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 注解大全2.11 JsonValue2.12 JsonKey2.13 JsonAnySetter2.14 JsonAnyGetter2.15 …

新华保险失速?业绩负增长,“欺骗投保人”问题屡罚屡犯

近日&#xff0c;新华保险&#xff08;601336.SH、01336.HK&#xff09;披露了2023年全年财报。贝多财经发现&#xff0c;该公司营收和利润出现双位数下滑&#xff0c;即便身处寿险市场回暖的大环境下&#xff0c;该公司今年以来的原保险保费收入也处于下跌态势。 即便新华保险…

Elastic 8.13:Elastic AI 助手中 Amazon Bedrock 的正式发布 (GA) 用于可观测性

作者&#xff1a;来自 Elastic Brian Bergholm 今天&#xff0c;我们很高兴地宣布 Elastic 8.13 的正式发布。 有什么新特性&#xff1f; 8.13 版本的三个最重要的组件包括 Elastic AI 助手中 Amazon Bedrock 支持的正式发布 (general availability - GA)&#xff0c;新的向量…

免费在线制流程图和思维导图ProcessOn高效协同

免费在线制流程图和思维导图ProcessOn高效协同&#xff0c;还有海量的免费模板。在ProcessOn&#xff0c;你可以与全球超过1.1亿的优秀人才一起合作&#xff0c;共同绘制出精美的流程图和思维导图。无论是工作中的项目管理&#xff0c;还是学习中的知识整理&#xff0c;Process…

GESP Python编程二级认证真题 2024年3月

Python 二级 2024 年 03 月 1 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 小杨的父母最近刚刚给他买了一块华为手表&#xff0c;他说手表上跑的是鸿蒙&#xff0c;这个鸿蒙是&#xff1f;&#xff08; &#xff09; A. 小程序 B. 计时器 C. 操作系统…