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.mzph.cn/news/779870.shtml

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

相关文章

【数据挖掘】实验5:数据预处理(2)

验5&#xff1a;数据预处理&#xff08;2&#xff09; 一&#xff1a;实验目的与要求 1&#xff1a;熟悉和掌握数据预处理&#xff0c;学习数据清洗、数据集成、数据变换、数据规约、R语言中主要数据预处理函数。 二&#xff1a;实验知识点总结 1&#xff1a;数据集成是将多个…

Topaz Video AI for mac 视频增强软件

Topaz Video AI for Mac是一款专为Mac用户设计的视频增强软件&#xff0c;它利用先进的人工智能技术和机器学习算法&#xff0c;为用户提供卓越的视频编辑和增强体验。 软件下载&#xff1a;Topaz Video AI for mac v4.2.2激活版 这款软件能够快速提高视频的清晰度、色彩饱和度…

激发数据潜力:企业数据中台的策略性构建与优化_光点科技

在信息时代&#xff0c;数据是企业价值链中不可或缺的一环。构建一个策略性的企业数据中台不仅能够整合分散的数据资源&#xff0c;还能提高决策效率和业务敏捷性。本文聚焦于如何策略性地构建和优化数据中台&#xff0c;以便企业能够最大化地利用数据资源&#xff0c;推动企业…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…

【Django学习笔记(二)】CSS语言介绍

CSS语言介绍 前言正文1、CSS 快速了解2、CSS 应用方式2.1 在标签上应用2.2 在head标签中写style标签2.3 写到文件中 3、问题探讨&#xff1a;用Flask框架开发不方便4、选择器4.1 ID选择器4.2 类选择器4.3 标签选择器4.4 属性选择器4.5 后代选择器4.6 注意事项 5、样式5.1 高度和…

【RISC-V】如何使用release的risc-v gnu toolchain

riscv64-elf-ubuntu-22.04-gcc-nightly-2024.03.01-nightly.tar.gz 首先去release页面中获取相应的压缩包 将压缩包解压到想解压的位置&#xff0c;这里我选择了 mv Downloads/riscv64-elf-ubuntu-22.04-gcc-nightly-2024.03.01-nightly.tar.gz riscv64-tool-chain/然后解压…

python 字典练习

# 字典练习1 import time def main():month_income{1月: 8000, 2月: 8200, 3月: 7900, 4月: 6900, 5月: 8900, 6月: 12000, 7月: 8900, 8月: 6000,9月: 8900, 10月: 9200, 11月: 6200, 12月: 7000}year_income0for k,v in month_income.items():print(月份→,k,工资→,v)time.s…

中国香港男歌手张国荣 明星网页成品 html人物明星网页设计制作 明星前端网页开发 网页期末设计制作作业成品

中国香港男歌手张国荣 7页面 人物明星主题 带设计说明 jquery图片轮播特效 滚动文字 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://ww…

【c++】类和对象(五)赋值运算符重载

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章带大家认识赋值运算符重载&#xff0c;const成员&#xff0c;取地址及const取地址操作符重载等内容 目录 1.赋值运算符重载1.1运算符重载1.1.1特性&#…

刷题之动态规划

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;开始刷动态规划的题目了&#xff0c;要特别注意初始化的时候给什么值。 动态规划5个步骤 状态表示 &#xff1a;dp数组中每一个下标对应值的含义是什么->dp[i]表示什么状态转移方程&#xff1a; dp[i] 等于什么1 和 2 是…

软考101-上午题-【信息安全】-网络安全

一、网络安全 1-1、安全协议 SSL(Secure Socket Layer&#xff0c;安全套接层)是 Netscape 于 1994年开发的传输层安全协议&#xff0c;用于实现 Web 安全通信。1996 年发布的 SSL3.0 协议草案已经成为一个事实上的Web 安全标准。 端口号是43。 SSL HTTP HTTPS TLS(Transpo…

XXE漏洞知识及ctfshow例题

XXE漏洞相关知识 XXE全称为XML Enternal Entity Injection 中文叫xml外部实体注入 什么是xml 简单了解XML&#xff1a; &#xff08;xml和html的区别可以简易的理解成&#xff1a;xml是用来储存数据和传输数据的而html是用来将数据展现出来&#xff09; XML 指可扩展标记语…

vue3封装Element导航菜单

1. 导航外层布局 AsideView.vue <template><el-menu:default-active"defaultActive"class"my-menu":collapse"isCollapse":collapse-transition"false"open"handleOpen"close"handleClose"><menu…

Mysql or与in的区别

创建一个表格 内涵一千万条数据 这张表中&#xff0c;只有id有建立索引&#xff0c;且其余都没有 测试1&#xff1a;使用or的情况下&#xff0c;根据主键进行查询 可以看到根据主键id进行or查询 花费了30-114毫秒&#xff0c;后面30多毫秒可能是因为Mysql的Buffer Pool缓冲池的…

大模型重塑电商,淘宝、百度、京东讲出新故事

配图来自Canva可画 随着AI技术日渐成熟&#xff0c;大模型在各个领域的应用也越来越深入&#xff0c;国内互联网行业也随之进入了大模型竞赛的后半场&#xff0c;开始从“百模大战”转向了实际应用。大模型从通用到细分垂直领域的跨越&#xff0c;也让更多行业迎来了新的商机。…

【Vue3源码学习】— CH2.6 effect.ts:详解

effect.ts&#xff1a;详解 1. 理解activeEffect1.1 定义1.2 通过一个例子来说明这个过程a. 副作用函数的初始化b. 执行副作用函数前c. 访问state.countd. get拦截器中的track调用e. 修改state.count时的set拦截器f. trigger函数中的依赖重新执行 1.3 实战应用1.4 activeEffect…

LLM推理入门指南②:深入解析KV缓存

在本系列文章《LLM推理入门指南①&#xff1a;文本生成的初始化与解码阶段》中&#xff0c;作者对Transformer解码器的文本生成算法进行了高层次概述&#xff0c;着重介绍了两个阶段&#xff1a;单步初始化阶段&#xff0c;即提示的处理阶段&#xff0c;和逐个生成补全词元的多…

浪潮信息AIStation与潞晨科技Colossal-AI 完成兼容性认证!

为进一步提升大模型开发效率&#xff0c;近年来&#xff0c;浪潮信息持续加强行业合作&#xff0c;携手业内头部&#xff0c;全面进攻大模型领域。日前&#xff0c;浪潮信息AIStation智能业务创新生产平台与潞晨科技Colossal-AI大模型开发工具完成兼容性互认证。后续&#xff0…

FFmpeg将绿幕视频处理成透明视频播放

怎么在网页端插入透明视频呢&#xff0c;之前在做Web3D项目时&#xff0c;使用threejs可以使绿幕视频透明显示在三维场景中&#xff0c;但是在网页端怎么让绿幕视频透明显示呢&#xff1f; 如图上图&#xff0c;视频背景遮挡住后面网页内容 想要如下图效果 之前有使用过ffmpeg…

机器人码垛机:智能仓储系统的重要组成部分

随着科技的飞速进步&#xff0c;机器人技术已经渗透到了许多行业领域&#xff0c;其中&#xff0c;仓储业尤为显著。机器人码垛机作为智能仓储系统的重要组成部分&#xff0c;不仅提高了码垛效率&#xff0c;还降低了人工成本和安全风险。然而&#xff0c;在其广泛应用的同时&a…