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,一经查实,立即删除!

相关文章

第九届蓝桥杯---航班时间python

1.总结&#xff1a; 时 秒数//3600 分 秒数%3600//60 秒 秒数%60 print(‘{:02d}:{:02d}:{:02d}’.format(res//3600, res%3600//60, res%60)) {:02d}表示输入的整数是两位不足前面补0&#xff0c;d表示整数 # 时间计算函数 def time_caculate(t):m1 t[0].split(":&q…

【数据挖掘】实验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;推动企业…

Java IO与NIO的对决:一场变革性的I/O架构较量及其实战演绎

在Java编程中&#xff0c;IO&#xff08;Input/Output&#xff09;和NIO&#xff08;New Input/Output&#xff09;是两种处理输入输出操作的关键API。虽然它们的目的都是为了进行数据的读写操作&#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 高度和…

x86_64 ubuntu22.04编译MetaRTC

metaRTC5.0 API https://github.com/metartc/metaRTC/wiki/metaRTC5.0-API Sample https://github.com/metartc/metaRTC/wiki/metaRTC5.0-API-Sample MetaRTC7.0编译 https://github.com/metartc/metaRTC/wiki/Here-we-come,-write-a-C-version-of-webRTC-that-runs-everywhere…

【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…

R语言数据分析基础(二)

R语言和Python的pandas库都用于数据处理和分析&#xff0c;但它们在语法和功能上有所不同。R语言主要用于统计计算和图形生成&#xff0c;而pandas则专注于数据处理和分析。 以下是一些R语言中实现pandas相似操作的方法&#xff1a; 数据框&#xff08;Data Frame&#xff09;…

【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…

uniapp中怎么引入echarts(最简单)

目录 引言 echarts.vue 文件代码 echarts-config 文件代码 在需要引入echarts图表的页面(.vue)中进行导入该文件(echarts.vue) 使用该组件(echarts) 引言 在uniapp中需要引入echarts时的时候,需要引入两个配置文件.分别是 echarts.vue 以及 echarts-config 放在你项目中…

XXE漏洞知识及ctfshow例题

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

Leetcode 680. 验证回文串 II

给你一个字符串 s&#xff0c;最多 可以从中删除一个字符。 请你判断 s 是否能成为回文字符串&#xff1a;如果能&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;s “aba” 输出&#xff1a;true 示例 2&#xff1a…

vue3封装Element导航菜单

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

springmvc实现文件上传功能

第一&#xff1a; RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException {//获取上传的文件的文件名String fileName photo.getOriginalFilename();//获取上传的文件的后缀名String suffixName fileName…