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改动而来,把音频图处理单独实现,并加以优化。

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

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

相关文章

如何选择采购管理软件解决方案:推荐与指南

选择一款合适自身企业采购模式的管理软件对于企业来说至关重要。一款好的采购管理软件不仅能够提高采购效率&#xff0c;还能降低采购成本、优化库存管理、增强供应链的透明度和可控性。而在选择采购管理软件时&#xff0c;大家还需要考虑以下几个关键因素&#xff1a; 一、需…

代码随想录训练营Day56:Leetcode647、516

Leetcode647&#xff1a; 问题描述&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 示例 1&#xff1a; 输入&#xff1a;s &q…

C/C++动态内存管理(new与delete)

目录 1. 一图搞懂C/C的内存分布 2. 存在动态内存分配的原因 3. C语言中的动态内存管理方式 4. C内存管理方式 4.1 new/delete操作内置类型 4.2 new/delete操作自定义类型 1. 一图搞懂C/C的内存分布 说明&#xff1a; 1. 栈区&#xff08;stack&#xff09;&#xff1a;在…

前端开发三大主流框架解析

Web前端三大主流框架分别是Angular、React和Vue.js。以下是《优联前端》关于这三个框架解析介绍&#xff1a; Angular&#xff1a; 来源与开发者&#xff1a;Angular是由Google开发的前端框架。功能特点&#xff1a;Angular是一个完整的框架&#xff0c;包括了数据绑定、组件化…

计组雨课堂(5)知识点总结——备考期末复习(xju)

在汇编语言源程序中&#xff0c;“微指令语句"不是常见的组成部分&#xff0c;因为微指令通常是在硬件层面进行处理的&#xff0c;而不是在汇编语言层面。因此&#xff0c;不属于汇编语言源程序的是"微指令语句”。在汇编语言中&#xff0c;组成指令语句和伪指令语句…

直方图滤波、粒子滤波、卡尔曼滤波

三者都是基于贝叶斯滤波。 粒子滤波和直方图滤波不要求高斯分布&#xff0c;可解决非线性 卡尔曼滤波要求高斯分布且线性。扩展卡尔曼滤波为了解决非线性问题&#xff0c;利用泰勒展开进行一阶近似。 直方图滤波就是贝叶斯滤波的直观实现。自动驾驶定位算法-直方图滤波(Hist…

什么是SpringCloud? --学习笔记

什么是SpringCloud&#xff1f; 想象一下&#xff0c;你要建一个现代化城市&#xff0c;这个城市由很多小区组成&#xff0c;每个小区承担着不同的职能&#xff0c;比如居住、购物、娱乐、教育等。在这个城市中&#xff0c;小区之间需要互相沟通协作&#xff0c;确保整个城市的…

【Linux终端探险】:从入门到熟练,玩转基础命令的秘密(二)

文章目录 &#x1f680;Linux基础命令&#xff08;二&#xff09;&#x1f308;1. 寻找目录/文件命令⭐2. 创建文件命令&#x1f44a;3. 网络接口查询命令❤️4. 打包命令&#x1f4a5;5. 解压命令 上期回顾&#xff1a; &#x1f525;&#x1f525;&#x1f525;【Linux终端探…

python 批量ts合并成一个mp4

首先&#xff0c;确保你已经安装了ffmpeg。 然后再次保证所有ts文件放在同一个文件夹中&#xff0c;并且依次命名为 1.ts 、 2.ts 、 3.ts 、 4.ts 、 4.ts 。。。 Python完整代码如下&#xff1a;(ffmpeg_batch_merge_ts.py文件) #!/usr/bin/python3 # -*- coding: UTF-8 -*…

19、matlab信号预处理中的中值滤波(medfilt1()函数)和萨维茨基-戈雷滤波滤(sgolayfilt()函数)

1、中值滤波&#xff1a;medfilt1()函数 说明&#xff1a;一维中值滤波 1&#xff09;语法 语法1&#xff1a;y medfilt1(x) 将输入向量x应用3阶一维中值滤波器。 语法2&#xff1a;y medfilt1(x,n) 将一个n阶一维中值滤波器应用于x。 语法3&#xff1a;y medfilt1(x,n…

2024年项目任务管理软件大盘点:12款值得一试的主流工具

12款优秀的项目任务管理软件&#xff1a;PingCode、Worktile、AIrTable、ClickUp、Teambition、Asana、Todoist、TAPD、Monday.com、Notion、Microsoft Project、Trello。 任务管理软件对于生活繁忙的人来说极为重要。它帮助用户有效跟踪他们需要完成的各项任务&#xff0c;包括…

mysql执行拼接的sql语句

在MySQL中&#xff0c;可以使用 CONCAT() 函数来拼接SQL语句。但是&#xff0c;请注意&#xff0c;直接拼接SQL语句可能会导致SQL注入问题&#xff0c;因此应当使用参数化查询来避免这个问题。 以下是一个使用 CONCAT() 函数拼接SQL语句的例子&#xff1a; SET tableName us…

牛客热题:没有重复数字的全排列

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;没有重复数字的全排列题目链接方…

LeetCode - 二分查找(Binary Search)算法集合(Python)[左右边界|旋转数组|双列表]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139419653 二分查找&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。其基本原理是将待搜索的区间分成两半&am…

小猪APP分发:如何轻松进行在线封装APP

你是否曾经因为需要封装一个新版本的APP而感到头疼&#xff1f;传统的封装过程往往繁琐且耗时。但是&#xff0c;别担心&#xff0c;现在有了“小猪APP分发”&#xff0c;一切变得如此简单。 小猪APP分发www.appzhu.net是一个专门提供在线APP封装服务的平台。无论你是开发者还…

Thingsboard物联网网关接入ThingsBoard物联网平台的操作说明

本文包含关于如何配置ThingsBoard 平台和连接钡铼技术R40设备的说明。ThingsBoard平台是一个用于数据收集、处理、可视化和设备管理的开源物联网平台。它通过行业标准MQTT协议实现设备连接。ThingsBoard结合了可扩展性、容错性和性能&#xff0c;因此您永远不会丢失数据。 4G L…

TP6 事件绑定、监听、订阅

事件绑定与监听 命令行生成事件类 php think make:event EventDemo 命令行生成事件监听类php think make:listener ListenerDemo 事件类可作为保存与事件相关信息的容器,如没有额外需求可以不需要定义 事件系统的所有操作都通过think\facade\Event类进行静态调用 当定义…

因为侵权只能重装系统后的必备软件List

前因&#xff1a;自从上次被adobe警告了后&#xff0c;又被source insight警告了… 正好得升级系统&#xff0c;不如直接清清爽爽的重装系统吧&#xff0c;一切回到解放前&#xff0c;重新配置电脑&#xff0c; 该文记载了我办公必备的宝贝软件们 1、Firefox-火狐浏览器&#x…

18、matlab信号生成与预处理--剔除异常值:hampel()函数

1、语法 说明&#xff1a;对输入向量x应用Hampel滤波器来检测和去除异常值。 1&#xff09;y hampel(x) 参数&#xff1a;x&#xff1a;输入信号 y:预处理的输出信号 对于x的每个样本&#xff0c;函数计算由样本及其周围的六个样本组成的窗口的中位数&#xff0c;每边三…

PHPStudy(xp 小皮)V8.1.1 通过cmd进入MySQL命令行模式

PHPStudy是一个PHP开发环境集成包&#xff0c;可用在本地电脑或者服务器上&#xff0c;该程序包集成最新的PHP/MySql/Apache/Nginx/Redis/FTP/Composer&#xff0c;一次性安装&#xff0c;无须配置即可使用。MySQL MySQL是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL A…