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://xiahunao.cn/news/2905972.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

硬件10、从网站获取封装

百度搜索IC封装网或者网址https://www.iclib.com/ 搜索想要的器件&#xff0c;直接下载他的原理图库和封装库

Spring Cloud+Spring Alibaba笔记

Spring CloudSpring Alibaba 文章目录 Spring CloudSpring AlibabaNacos服务发现配置中心 OpenFeign超时机制开启httpclient5重试机制开启日志 SeataSentinel流量控制熔断降级热点控制规则持久化集成 OpenFeign集成 Gateway MicrometerZipKinGateway路由断言过滤器 Nacos 服务…

【YOLOv5改进系列(6)】高效涨点----使用DAMO-YOLO中的Efficient RepGFPN模块替换yolov5中的Neck部分

文章目录 &#x1f680;&#x1f680;&#x1f680;前言一、1️⃣ 添加yolov5_GFPN.yaml文件二、2️⃣添加extra_modules.py代码三、3️⃣yolo.py文件添加内容3.1 &#x1f393; 添加CSPStage模块 四、4️⃣实验结果4.1 &#x1f393; 使用yolov5s.pt训练的结果对比4.2 ✨ 使用…

【Pytorch入门】小土堆PyTorch入门教程完整学习笔记(详细笔记并附练习代码 ipynb文件)

小土堆PyTorch入门教程笔记 最近在观看PyTorch深度学习快速入门教程&#xff08;绝对通俗易懂&#xff01;&#xff09;【小土堆】顺便做点笔记&#xff0c;方便回看&#xff0c;同时也希望记录的笔记能够帮助到更多在入门的小伙伴~ 【注】仅记录个人觉得重要的知识&#xff0c…

Java的静态代理与jdk动态代理

代理 我们经常利用代理进行解耦以及控制对实际对象的访问等工作。例如&#xff0c;我们可以通过代理对方法的调用进行更精细的控制&#xff08;例如加上日志、权限控制等&#xff09;&#xff0c;而无需修改实际对象的代码。代理的作用是无侵入式的给代码增加功能。有些事情是…

sql——对于行列转换相关的操作

目录 一、lead、lag 函数 二、wm_concat 函数 三、pivot 函数 四、判断函数 遇到需要进行行列转换的数据处理需求&#xff0c;以 oracle 自带的表作为例子复习一下&#xff1a; 一、lead、lag 函数 需要行列转换的表&#xff1a; select deptno,count(empno) emp_num from…

第十四届蓝桥杯JavaA组省赛真题 - 互质数的个数

解题思路&#xff1a; 快速幂 欧拉函数 快速幂比较常见于数据较大的取模场景&#xff0c;欧拉函数感觉还是有点抽象 注意&#xff1a; 取模的时候就不要简写了&#xff0c;例如&#xff1a;res res * a % mod;不要写成res * a % mod; import java.util.Scanner;public c…

算法之美:B+树原理、应用及Mysql索引底层原理剖析

B树的一种变种形式&#xff0c;B树上的叶子结点存储关键字以及相应记录的地址&#xff0c;同等存储空间下比B-Tree存储更多Key。非叶子节点不对关键字记录的指针进行保存&#xff0c;只进行数据索引 , 树的层级会更少 , 所有叶子节点都在同一层, 叶子节点的关键字从小到大有序排…

直流马达驱动芯片D6289ADA介绍

应用领域 适用于智能断路器&#xff08;家用或工业智能空开&#xff09;、新能源汽车充电枪锁、电动玩具、电磁门锁、自动阀门等的直流电机驱动。 功能介绍 D6289ADA是一款直流马达驱动芯片&#xff0c;它有两个逻辑输入端子用来控制电机前进、后退及制动。该电路具有良好的抗干…

如何解决 IntelliJ IDEA 中属性文件的编码问题

在使用 IntelliJ IDEA 进行开发过程中&#xff0c;我们经常会遇到属性文件&#xff08;.properties 文件&#xff09;的编码问题。如果属性文件的编码设置不正确&#xff0c;就会导致中文等特殊字符显示乱码。这是因为IntelliJ IDEA中默认的配置文件的编码格式是ISO-8859-1。 …

36-递归与迭代

36-1 用递归和迭代解决问题 1、求n的阶乘 公式&#xff1a; n!123...(n-1)n。用递归方式定义&#xff1a;0!1&#xff0c;n!(n-1)!n。 代码1&#xff1a; 我们先回忆一下之前用循环怎么实现的吧 非递归&#xff0c;也可称迭代&#xff1a; int main() {int n 0;scanf(&q…

精酿啤酒:酿造工艺的创新与实验探索

在啤酒酿造领域&#xff0c;创新与实验探索一直是推动品质提升和品类丰富的重要动力。Fendi Club啤酒作为一家注重品质和口感的品牌&#xff0c;在酿造工艺方面不断创新与尝试&#xff0c;为消费者带来更多与众不同的风味体验。 Fendi Club啤酒在原料选择方面不断进行创新与实验…

超级码科技股份携手品品香开数字茶业新范式,实现全产业链数智化闭环

品品香白茶创立于1992年&#xff0c;品牌创立的30多年间&#xff0c;品品香不断创新技术、精耕细作、推陈出新&#xff0c;在不同发展时期始终走在行业前沿&#xff0c;助推着白茶产业高质量发展。 2016年&#xff0c;品品香发挥茶产业龙头示范作用率先进行转型&#xff0c;联…

jmeter中参数加密

加密接口常用的方式有&#xff1a; MD5&#xff0c;SHA&#xff0c;HmacSHA RSA AES&#xff0c;DES&#xff0c;Base64 压测中有些参数需要进行加密&#xff0c;加密方式已接口文档为主。 MD5加密 比如MD5加密的接口文档&#xff1a; 请求URL&#xff1a;http://101.34.221…

免费VPS/云服务器整理汇总

随着互联网的普及和云计算技术的飞速发展&#xff0c;越来越多的人开始尝试使用VPS&#xff08;Virtual Private Server&#xff0c;虚拟专用服务器&#xff09;或者云服务器来部署自己的在线业务。本文将对免费VPS/云服务器进行整理汇总&#xff0c;助力大家轻松开启云计算之旅…

顺应互联网发展大潮流,红河农资招商火爆开启

顺应互联网发展大潮流&#xff0c;红河农资招商火爆开启 进入新世纪&#xff0c;生态农业建设成为了影响和改变农村、农业工作的重要领域。尤其是在互联网的快速发展之下&#xff0c;实现农业结构调整&#xff0c;推动互联网模式的发展&#xff0c;成为了当前生态农业发展的主流…

​python学习之变量类型​

print单纯输中的十种数据类型只需要用print()函数即可&#xff0c;()里面直接写变量名。 下面重点介绍print格式输出&#xff1a; 第一种方法&#xff1a;一个萝卜一个坑&#xff0c;下面的代码中&#xff0c;{0}、{1}、{2}分别表示j,i,j*i&#xff0c;单引号里面是输出格式。…

网络七层模型之表示层:理解网络通信的架构(六)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Python中模块

基本概念 **模块 module&#xff1a;**一般情况下&#xff0c;是一个以.py为后缀的文件 ①Python内置的模块&#xff08;标准库&#xff09;&#xff1b; ②第三方模块&#xff1b; ③自定义模块。 包 package&#xff1a; 当一个文件夹下有 init .py时&#xff0c;意为该文…

构建ELK+Filebeat+kafka+zookeeper大数据日志分析平台

主机IP 角色 所属服务层 部署服务 192.168.11.11 日志生产 采集层 filebeat 192.168.11.12 日志缓存 数据处理层、缓存层 Zookeeperkafkalogstash 192.168.11.13 192.168.11.14 日志展示 持久、检索、展示层 Logstashelasticsearchkibana 数据流向 filebeat--…