当前位置: 首页 > news >正文

QT 音乐播放器【一】 显示音频级别指示器

文章目录

      • 效果图
      • 概述
      • 代码
      • 总结

效果图

在这里插入图片描述


概述

  • QMediaPlayer就不介绍了,就提供了一个用于播放音频和视频的媒体播放器

  • QAudioProbe 它提供了一个探针,用于监控音频流。当音频流被捕获或播放时,QAudioProbe 可以接收到音频数据。这个类在需要访问音频数据以进行分析或处理的情况下非常有用,而不需要直接与音频设备交互。

  • audioBufferProbedQAudioProbe 的一个信号,当音频数据可用时这个信号会被发射。这个信号的参数是一个 QAudioBuffer 对象,它包含了音频数据的详细信息,比如采样率、通道数、格式以及音频数据本身。当 QAudioProbe 与一个 QMediaPlayer,它可以探测到这个媒体对象的音频输出。当媒体对象播放音频时,音频数据会通过 audioBufferProbed 信号传递槽函数,通过槽函数处理音频缓冲区,更新音频级别显示器。

        player = new QMediaPlayer(this);auto m_audioHistogram = new HistogramWidget(this);auto probe = new QAudioProbe(this);connect(probe, &QAudioProbe::audioBufferProbed, m_audioHistogram, &HistogramWidget::processBuffer);probe->setSource(player);
    
  • 还有一个关键点就是分析给定的QAudioBuffer对象,计算每个通道的峰值电平,从而获取音频缓冲区的电平值。

  • 通过得到的电平值利用paintEvent将其绘制出来,并采用QLinearGradient实现渐变色使得更加美观。


代码

  • 直接把cpp代码都贴出来,做了很详细的注释,篇幅限制就不把类声明贴出,没有特殊处理。
  #include "HistogramWidget.h"#include <QPainter>#include <QHBoxLayout>template <class T>static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels);/*** 获取音频格式的最大峰值值。** 此函数根据给定的QAudioFormat对象参数,计算并返回一个表示该音频格式下理论上的最大值。* 主要用于支持PCM格式的音频,对非PCM格式或无效的格式,函数将返回0。** @param format QAudioFormat对象,包含待检查的音频格式信息。* @return 返回一个qreal类型值,表示音频格式的最大峰值。对于不支持的格式或无效的参数,返回0。*/qreal getPeakValue(const QAudioFormat &format){// 检查音频格式是否有效if (!format.isValid())return qreal(0);// 检查音频编码是否为PCMif (format.codec() != "audio/pcm")return qreal(0);// 根据样本类型计算峰值值switch (format.sampleType()){case QAudioFormat::Unknown:break;case QAudioFormat::Float:// 对于浮点样本,只支持32位,且返回一个略大于1的值if (format.sampleSize() != 32)return qreal(0);return qreal(1.00003);case QAudioFormat::SignedInt:// 对于有符号整数样本,根据样本大小返回相应的最大值if (format.sampleSize() == 32)return qreal(INT_MAX);if (format.sampleSize() == 16)return qreal(SHRT_MAX);if (format.sampleSize() == 8)return qreal(CHAR_MAX);break;case QAudioFormat::UnSignedInt:// 对于无符号整数样本,根据样本大小返回相应的最大值if (format.sampleSize() == 32)return qreal(UINT_MAX);if (format.sampleSize() == 16)return qreal(USHRT_MAX);if (format.sampleSize() == 8)return qreal(UCHAR_MAX);break;}// 如果没有匹配到任何已知情况,返回0return qreal(0);}template <class T>/*** 获取缓冲区中每个通道的最大值。** @param buffer 指向音频帧数据的指针,数据类型为T,假设为原始音频样本。* @param frames 音频帧的数量。* @param channels 音频的通道数。* @return QVector<qreal> 返回一个包含每个通道最大值的向量。*/QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels){// 初始化一个向量来保存每个通道的最大值,初始值为0。QVector<qreal> max_values;max_values.fill(0, channels);// 遍历所有帧for (int i = 0; i < frames; ++i){// 遍历当前帧中的每个通道for (int j = 0; j < channels; ++j){// 计算当前样本的绝对值qreal value = qAbs(qreal(buffer[i * channels + j]));// 如果当前样本值大于当前通道的最大值,则更新最大值if (value > max_values.at(j))max_values.replace(j, value);}}return max_values;}/*** 获取音频缓冲区的电平值。** 该函数分析给定的QAudioBuffer对象,计算每个通道的峰值电平,并返回一个包含每个通道当前电平值的向量。* 电平值是相对于缓冲区中找到的峰值电平的标准化值,使得缓冲区中的最大值为1。** @param buffer QAudioBuffer对象,包含要分析的音频数据。* @return QVector<qreal> 包含每个通道电平值的向量。如果无法分析缓冲区,则返回空向量。*/QVector<qreal> getBufferLevels(const QAudioBuffer &buffer){QVector<qreal> values;// 如果缓冲区无效,则直接返回空向量if (!buffer.isValid())return values;// 检查音频格式是否有效,且是否为小端序if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian)return values;// 检查音频编解码器是否为PCMif (buffer.format().codec() != "audio/pcm")return values;int channelCount = buffer.format().channelCount();values.fill(0, channelCount);qreal peak_value = getPeakValue(buffer.format());// 如果无法计算峰值电平,则返回空向量if (qFuzzyCompare(peak_value, qreal(0)))return values;// 根据样本类型和大小,计算每个通道的电平值switch (buffer.format().sampleType()){case QAudioFormat::Unknown:case QAudioFormat::UnSignedInt:// 处理无符号整型样本,支持32位、16位和8位if (buffer.format().sampleSize() == 32)values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount);if (buffer.format().sampleSize() == 16)values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount);if (buffer.format().sampleSize() == 8)values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount);// 标准化电平值for (int i = 0; i < values.size(); ++i)values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2);break;case QAudioFormat::Float:// 处理浮点型样本,支持32位if (buffer.format().sampleSize() == 32){values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount);// 标准化电平值for (int i = 0; i < values.size(); ++i)values[i] /= peak_value;}break;case QAudioFormat::SignedInt:// 处理有符号整型样本,支持32位、16位和8位if (buffer.format().sampleSize() == 32)values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount);if (buffer.format().sampleSize() == 16)values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount);if (buffer.format().sampleSize() == 8)values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount);// 标准化电平值for (int i = 0; i < values.size(); ++i)values[i] /= peak_value;break;}return values;}QAudioLevel::QAudioLevel(QWidget *parent): QWidget(parent){setMinimumHeight(15);setMaximumHeight(50);}void QAudioLevel::setLevel(qreal level){if (m_level != level){m_level = level;update();}}void QAudioLevel::paintEvent(QPaintEvent *event){Q_UNUSED(event);QPainter painter(this);// 渐变色QLinearGradient gradient(0, 0, width(), height());int hue = static_cast<int>(m_level * 360.0);gradient.setColorAt(m_level, QColor::fromHsl(hue, 255, 127));// 定义每个小矩形的间距const int padding = 1;// 定义总共有多少个小矩形const int numRects = 50;// 计算每个矩形的宽度qreal singleRectWidth = (width() - (numRects + 1) * padding) / numRects;// 使用m_level计算需要绘制多少个小矩形int activeRects = qRound(m_level * numRects);painter.setBrush(QBrush(gradient));for (int i = 0; i < activeRects; ++i){qreal rectLeft = i * (singleRectWidth + padding) + padding;QRectF rect(rectLeft, 0, singleRectWidth, height());painter.drawRect(rect);}// 绘制剩余的小矩形(不活跃部分)painter.setBrush(Qt::black);for (int i = activeRects; i < numRects; ++i){qreal rectLeft = i * (singleRectWidth + padding) + padding;QRectF rect(rectLeft, 0, singleRectWidth, height());painter.drawRect(rect);}}HistogramWidget::HistogramWidget(QWidget *parent) : QWidget(parent){setLayout(new QVBoxLayout);}HistogramWidget::~HistogramWidget(){}/*** 处理音频缓冲区,更新音频级别显示器。** @param buffer QAudioBuffer对象,包含待处理的音频数据。*/void HistogramWidget::processBuffer(const QAudioBuffer &buffer){// 检查音频级别计数是否与音频缓冲区的声道数匹配if (m_audioLevels.count() != buffer.format().channelCount()){// 如果不匹配,则删除现有音频级别对象,并根据声道数创建新的音频级别对象qDeleteAll(m_audioLevels);m_audioLevels.clear();for (int i = 0; i < buffer.format().channelCount(); ++i){QAudioLevel *level = new QAudioLevel(this);m_audioLevels.append(level);layout()->addWidget(level);}}// 计算音频缓冲区的级别并更新音频级别显示器QVector<qreal> levels = getBufferLevels(buffer);for (int i = 0; i < levels.count(); ++i){m_audioLevels.at(i)->setLevel(levels.at(i));}}void HistogramWidget::paintEvent(QPaintEvent *event){Q_UNUSED(event);if (!m_audioLevels.isEmpty())return;QPainter painter(this);painter.fillRect(0, 0, width(), height(), QColor::fromRgb(0, 0, 0));}

总结

  • 学习Qt demo中的Media Player Example改动而来,把音频图处理单独实现,并加以优化。

相关文章:

QT 音乐播放器【一】 显示音频级别指示器

文章目录 效果图概述代码总结 效果图 概述 QMediaPlayer就不介绍了&#xff0c;就提供了一个用于播放音频和视频的媒体播放器 QAudioProbe 它提供了一个探针&#xff0c;用于监控音频流。当音频流被捕获或播放时&#xff0c;QAudioProbe 可以接收到音频数据。这个类在需要访问…...

【MATLAB源码-第220期】基于matlab的Massive-MIMO误码率随着接收天线变化仿真采用ZF均衡和QPSK调制。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 系统背景与目标 无线通信系统的发展极大地推动了现代通信技术的进步&#xff0c;从移动通信到无线局域网&#xff0c;甚至是物联网&#xff0c;均依赖于无线通信系统的高效和可靠性。在无线通信系统中&#xff0c;核心目…...

【前端】政务服务大数据可视化监控平台(源码+html+css+js)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…...

【网关】工业智能网关-02

一 公司简介 保定飞凌嵌入式技术有限公司始于2006年&#xff0c;是一家专注嵌入式核心控制系统研发、设计和生产的高新技术企业&#xff0c;是国内最早专业从事嵌入式技术的企业之一。 经过十几年的发展与积累&#xff0c;公司拥有业内一流的软硬件研发团队&#xff0c;在北京…...

【C语言】动态内存管理技术文档

【C语言】动态内存管理技术文档 目录 【C语言】动态内存管理技术文档 一、内存管理基础...

低空经济的意义所在

发展低空经济对于推动经济发展、促进产业升级、降低运输成本、构建综合交通系统等方面都具有重要意义。低空经济对推动经济发展提供新动能。低空经济作为新兴产业&#xff0c;具有巨大的发展潜力&#xff0c;能够带动投资、促进消费&#xff0c;为经济增长注入新动力。除此之外…...

DNF手游攻略:0氪攻略,转职技巧与避坑指南!

在DNF手游的冒险旅程中&#xff0c;角色的转职是一次重要的成长经历。通过转职&#xff0c;玩家可以获得全新的技能和属性&#xff0c;提升自己在地下城中的战斗力。本文将为您介绍转职后的关键技巧和日常任务&#xff0c;帮助您更好地适应新的职业身份&#xff0c;成为地下城中…...

周报 | 24.5.27-24.6.2文章汇总

为了更好地整理文章和发表接下来的文章&#xff0c;以后每周都汇总一份周报。 周报 | 24.5.20-24.5.26文章汇总-CSDN博客 集智书童 | YOLOv10开源&#xff5c;清华用端到端YOLOv10在速度精度上都生吃YOLOv8和YOLOv9_yolov8 yolov10-CSDN博客 机器之心 | 清华接手&#xff0c…...

【C++初阶学习】第十二弹——stack和queue的介绍和使用

C语言栈&#xff1a;数据结构——栈(C语言版)-CSDN博客 C语言队列&#xff1a;数据结构——队列&#xff08;C语言版&#xff09;-CSDN博客 前言&#xff1a; 在之前学习C语言的时候&#xff0c;我们已经学习过栈与队列&#xff0c;并学习过如何使用C语言来实现栈与队列&…...

nginx反向代理了解

文章目录 Nginx反向代理反向代理系统调优Proxy Buffer相关指令 Nginx 具有高性能的http和反向代理的web服务器&#xff0c;同时也是一个pop3/smtp/imap代理服务器&#xff0c;使用c语言编写 **Web服务器&#xff1a;**也叫网页服务器&#xff0c;web server&#xff0c;主要功…...

插入排序和希尔排序

目录 1.直接插入排序2.希尔排序 1.直接插入排序 基本思想&#xff1a; 把待排序的数据按其大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的数据插入完成为止。 当插入第i个元素时&#xff0c;前面的a[0],a[1],...,a[i-1]个数据已经排好序了&#xff0c;此时用…...

Java web应用性能分析之【java进程问题分析定位】

Java web应用性能分析之【java进程问题分析概叙】-CSDN博客 Java web应用性能分析之【java进程问题分析工具】-CSDN博客 Java web应用性能分析之【jvisualvm远程连接云服务器】-CSDN博客 由于篇幅限制、前面三篇讲了准备工作和分析小结&#xff0c;这里将详细操作java进程问题…...

c#控件笔记

c# PictureBox在工具箱的哪个位置 在 Visual Studio 的工具箱中&#xff0c;PictureBox 控件位于 “Common Controls” 部分。要找到 PictureBox&#xff0c;请按照以下步骤操作&#xff1a; 打开 Visual Studio 并加载您的项目。确保已经打开了设计器视图&#xff08;即您的…...

STM32-15-DMA

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD 文章目录 STM…...

Go语言 几种常见的IO模型用法 和 netpoll与原生GoNet对比

【go基础】16.I/O模型与网络轮询器netpoller_go中的多路io复用模型-CSDN博客 字节开源的netPoll多路复用器源码解析-CSDN博客 一、几种常见的IO模型 1. 阻塞I/O (1) 解释&#xff1a; 用户调用如accept、read等系统调用&#xff0c;向内核发起I/O请求后&#xff0c;应用程序…...

大米cms安装支付逻辑漏洞

1.安装 下载来源&#xff1a;https://www.cnblogs.com/xfbk/p/17910054.html 链接&#xff1a;https://pan.baidu.com/s/1b-Z6RaFBZ6CsSIErY46Pyg?pwdq8qq 提取码&#xff1a;q8qq 注意一下配置就可以了&#xff1a;php5.5apachemysql5.0&#xff0c;主要就是数据库版本要注…...

使用 zxing 生成二维码以及条形码

需求背景 前期在做项目的时候&#xff0c;有一个需求是说要生成一张条形码&#xff0c;并且呢将条形码插入到 excel 中去&#xff0c;但是之前一直没有搞过找个条形码或者是二维码&#xff0c;最后是做出来了&#xff0c;这里呢就先看看怎么生成&#xff0c;后面再抽时间来写写…...

发布 jar 包到 maven 中央仓库

目前开发基本都是以maven或者gradle的方式,直接引入依赖包即可,那么该咋那么发布我们自己的jar包到maven仓库,让别人使用呢?本文适用于2024.3之后的步骤 文章目录 账号准备第一步,注册账号第二步,新建命名空间第三步,验证命名空间第四步,创建 push 的账号和密码点击右…...

AI智能体研发之路-模型篇(四):一文入门pytorch开发

博客导读&#xff1a; 《AI—工程篇》 AI智能体研发之路-工程篇&#xff08;一&#xff09;&#xff1a;Docker助力AI智能体开发提效 AI智能体研发之路-工程篇&#xff08;二&#xff09;&#xff1a;Dify智能体开发平台一键部署 AI智能体研发之路-工程篇&#xff08;三&am…...

英语口语中though的用法(even though、as though)

文章目录 英语口语中 "though" 的用法详解1. "Though" 作为转折连词的用法1.1 基本用法示例句子&#xff1a; 1.2 位置灵活性示例句子&#xff1a; 2. "Though" 作为副词的用法2.1 表示对比或转折示例句子&#xff1a; 2.2 强调前述观点示例句子…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

在Zenodo下载文件 用到googlecolab googledrive

方法&#xff1a;Figshare/Zenodo上的数据/文件下载不下来&#xff1f;尝试利用Google Colab &#xff1a;https://zhuanlan.zhihu.com/p/1898503078782674027 参考&#xff1a; 通过Colab&谷歌云下载Figshare数据&#xff0c;超级实用&#xff01;&#xff01;&#xff0…...

用 FFmpeg 实现 RTMP 推流直播

RTMP&#xff08;Real-Time Messaging Protocol&#xff09; 是直播行业中常用的传输协议。 一般来说&#xff0c;直播服务商会给你&#xff1a; ✅ 一个 RTMP 推流地址&#xff08;你推视频上去&#xff09; ✅ 一个 HLS 或 FLV 拉流地址&#xff08;观众观看用&#xff09;…...