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

QT 音乐播放器【二】 歌词同步+滚动+特效

文章目录

      • 效果图
      • 概述
      • 代码
        • 解析歌词
        • 歌词同步
        • 歌词特效
      • 总结

效果图

在这里插入图片描述


概述

  • 先整体说明一下这个效果的实现,你所看到的歌词都是QGraphicsObject,在QGraphicsView上绘制(paint)出来的。也就是说每一句歌词都是一个图元(item)
  1. 为什么用QGraphicsView框架?

    • 在做歌词滚动效果时,我看网上实现这一效果基本上都是用QLabel,这样或许简单很多,但是效果单一,且不够灵活。使用图形视图这套,图形项可以自由地被移动、缩放、旋转和编辑。当然主要还是为了提升自己,可以更熟悉这套框架。
  2. 如何解析歌词?

    • 这里解析的是lrc文件为一般的歌词文件,格式如下:格式是固定的,那么就可以通过正则表达式来解析。然后存放在一个QMap中,key是时间,value是歌词。
    [02:08.496]乌蒙山连着山外山
    [02:11.138]月光洒下了响水滩
    [02:13.993]有没有人能告诉我
    [02:16.487]可是苍天对你在呼唤
    
  3. 如何同步歌词?

    • QMediaPlayer中有一个信号positionChanged,播放音乐时会时刻刻触发,可以获取当前播放时间。然后和前面我们存放在QMap中的时间进行对比,所以QMap存放的时间格式要按positionChanged发出的时间格式来解析。但我试验过很多次俩者的时间都是无法精确相等的。这里采取的方案是遍历QMap,找到第一个时间大于等于positionChanged发出的时间,然后获取这个时间对应的歌词,这便是当前的歌词。然后通过当前的key在获取前后几句的歌词。
  4. 歌词滚动以及那些特效如何实现的?

    • 同步歌词的时候会获取当前歌词以及前后几句歌词,提前存好对应歌词的特效,如下:这里面存了一个QMap,里面存放了每一句歌词的属性,包括字体大小,位置,透明度等等。
     m_textMapInfolst << QMap<QString, QString>{
    {"index", "1"},
    {"font", "12"},
    {"y", "-100"},
    {"x", "300"},
    {"opacity", "0.2"}};
    

    我这里有七句歌词,那么就存七个QMap在一个QList中,当歌词刷新的时候就去遍历,根据QMap中的属性来设置item歌词,这里的图元要自己实现,重写paint函数。


代码

解析歌词
  • 解析的时候把格式设置GB 2312,不然会是乱码。按行已经QMediaPlayer的时间格式读取数据,并全部存放到listLyricsMap中。
bool Lyrics::readLyricsFile(QString lyricsPath)
{listLyricsMap.clear();QFile file(lyricsPath);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){listLyricsMap.clear();return false;}QTextStream in(&file);in.setCodec("GB 2312");QString line;while (!in.atEnd()){line = in.readLine();analysisLyricsFile(line);}return true;
}bool Lyrics::analysisLyricsFile(QString line)
{if (line == NULL || line.isEmpty()){return false;}QRegExp timeRegExp("\\[(\\d+):(\\d+\\.\\d+)\\]");if (timeRegExp.indexIn(line) != -1){qint64 totalTime = timeRegExp.cap(1).toInt() * 60000 + // 分钟timeRegExp.cap(2).toFloat() * 1000; // 秒QString lyricText = line.mid(timeRegExp.matchedLength());listLyricsMap.insert(totalTime, lyricText);}return true;
}
歌词同步
  • 绑定信号
    connect(player, SIGNAL(positionChanged(qint64)),this, SLOT(updateTextTime(qint64)));
  • 读取对应listLyricsMap中的歌词

void MainWindow::updateTextTime(qint64 position)
{auto lrcMap = lyric->getListLyricsMap();qint64 previousTime = 0;qint64 currentLyricTime = 0;QMapIterator<qint64, QString> i(lrcMap);while (i.hasNext()){i.next();if (position < i.key()){QString currentLyric = lrcMap.value(previousTime);currentLyricTime = previousTime;break;}previousTime = i.key();}QStringList displayLyrics; // 存储将要显示的歌词列表。// 获取将要显示的歌词QMap<qint64, QString>::iterator it = lrcMap.find(currentLyricTime);// 显示前三句,如果it不是开头,就向前移动迭代器for (int i = 0; i < 3 && it != lrcMap.begin(); i++){--it;displayLyrics.prepend(it.value());}// 重置迭代器it = lrcMap.find(currentLyricTime);QString currntStr = QString();// 显示当前句if (it != lrcMap.end()){currntStr = QString("<font color='red'>" + it.value() + "</font>");displayLyrics.append(it.value());}// 显示后三句for (int i = 0; i < 3 && it != lrcMap.end(); i++){++it;if (it != lrcMap.end()){displayLyrics.append(it.value());}}//更新显示imageViewWindow->textChanged(displayLyrics);
}
歌词特效
  • 同步于上述歌词的改动,清空场景遍历特效m_textMapInfolst,重新进行图元绘制
void ImageViewWindow::textChanged(QStringList &lsit)
{m_scene->clear();for (int index = 0; index < m_textMapInfolst.size(); index++){const auto textInfoMap = m_textMapInfolst[index];GraphicsText *item = new GraphicsText();item->setStr(lsit[index]);item->setStrFont(textInfoMap["font"].toInt());item->setItemOffset(QPointF(textInfoMap["x"].toInt() + image_xoffset, textInfoMap["y"].toInt() + image_yoffset));item->setZValue(textInfoMap["index"].toInt());item->setOpacity(textInfoMap["opacity"].toFloat());m_items << item;m_scene->addItem(item);}
}
  • 自绘图元
void GraphicsText::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{painter->setFont(m_font);if (m_font.pointSize() > 20){painter->setPen(QPen(Qt::red));}else{painter->setPen(QPen(Qt::blue));}painter->drawText(offset, str);
}

总结

  • 实现这个功能遇到的问题挺多的,比如绘制文本的时候只有一根线显示,是要view设置setViewportUpdateMode(QGraphicsView::FullViewportUpdate),类似的问题挺多,还不好找。
  • 歌词特效这块还可以再扩展,字体,入场效果等都可以设置。
  • 当然这个功能还有很多可以优化的地方,BUG或许也不少,实现标题的功能的逻辑就是如上,可以作为参考。

相关文章:

QT 音乐播放器【二】 歌词同步+滚动+特效

文章目录 效果图概述代码解析歌词歌词同步歌词特效 总结 效果图 概述 先整体说明一下这个效果的实现&#xff0c;你所看到的歌词都是QGraphicsObject&#xff0c;在QGraphicsView上绘制(paint)出来的。也就是说每一句歌词都是一个图元(item)。 为什么用QGraphicsView框架&…...

关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备(改进版)

主要最近做了一个要用STM32实现读取鼠标键盘一体的那种USB设备&#xff0c;STM32的界面上要和电脑一样的能通过这个USB接口实现鼠标移动&#xff0c;键盘的按键。然后我就很自然的去参考了正点原子的例程&#xff0c;可是找了一圈&#xff0c;发现正点原子好像用的库函数&#…...

Soildworks学习笔记(二)

放样凸台基体&#xff1a; 自动生成连接两个物体两个面的基体&#xff1a; 2.旋转切除&#xff1a; 3.剪切实体&#xff1a; 4.转换实体引用&#xff1a; 将实体的轮廓线转换至当前草图使其成为当前草图的图元,主要用于在同一平面或另一个坐标中制作草图实体或其尺寸的副本。 …...

Linux配置uwsgi环境

Linux配置uwsgi环境 1.进入虚拟环境 source /envs/django_-shop-system/bin/activate2.安装uwsgi pip install uwsgi3.基于uwsgi运行项目 – 基于配置文件 在项目目录下创建配置文件 #socket 0.0.0.0:8005 http 0.0.0.0:8005 # http120.55.47.111:8005 chdir/opt/www/djang…...

Nagios的安装和使用

*实验* *nagios安装和使用* Nagios 是一个监视系统运行状态和网络信息的监视系统。Nagios 能监视所指定的本地或远程主机以及服务&#xff0c;同时提供异常通知功能等. Nagios 可运行在 Linux/Unix 平台之上&#xff0c;同时提供一个可选的基于浏览器的 WEB 界面以方便系统管…...

Numba 的 CUDA 示例(4/4):原子和互斥

本教程为 Numba CUDA 示例 第 4 部分。 本系列第 4 部分总结了使用 Python 从头开始学习 CUDA 编程的旅程 介绍 在本系列的前三部分&#xff08;第 1 部分&#xff0c;第 2 部分&#xff0c;第 3 部分&#xff09;中&#xff0c;我们介绍了 CUDA 开发的大部分基础知识&#xf…...

【机器学习】机器学习引领AI:重塑人类社会的新纪元

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀机器学习引领AI &#x1f4d2;1. 引言&#x1f4d5;2. 人工智能&#xff08;AI&#xff09;&#x1f308;人工智能的发展&#x1f31e;应用领…...

【制作面包game】

编写一个制作面包的游戏代码涉及到游戏设计、编程和用户界面设计等多个方面。这里我可以提供一个简化版本的Python代码示例&#xff0c;用于创建一个基本的面包制作游戏。这个游戏将会有一个简单的用户界面&#xff0c;玩家可以通过输入命令来制作面包。 游戏的基本流程如下&a…...

Django更改超级用户密码

Django更改超级用户密码 1、打开shell 在工程文件目录下敲入&#xff1a; python manage.py shell再在python交互界面输入&#xff1a; from django.contrib.auth.models import User user User.objects.get(username root) user.set_password(123456) user.save()其中ro…...

ROS基础学习-ROS通信机制进阶

ROS通信机制进阶 目录 0.简介1.常用API1.1 节点初始化函数1.1.1 C++1.1.2 Python1.2 话题与服务相关函数1.2.1 对象获取相关1.2.1.1 C++1.2.1.2 Python1.2.2 订阅对象相关1.2.2.1 C++1.2.2.2 Python1.2.3 服务对象相关函数1.2.3.1 C++1.2.3.2 Python1.2.4 客户端对象相关1.2.4.…...

【Vue3】shallowReactive() and shallowReadonly()

历史小剧场 所谓历史&#xff0c;就是过去的事&#xff0c;它的残酷之处在于&#xff1a;无论你哀嚎&#xff0c;悲伤&#xff0c;痛苦&#xff0c;落寞&#xff0c;追悔&#xff0c;它都无法改变。 一具有名的尸体躺在无数无名的尸体上&#xff0c;这就是所谓的霸业。---- 《明…...

【javaEE初阶】

&#x1f308;&#x1f308;&#x1f308;关于java ⚡⚡⚡java的由来 我们这篇文章主要是来介绍javaEE&#xff0c;一般称为java企业版&#xff0c;实际上java的历史可以追溯到上个世纪90年代&#xff0c;当时主要的语言主流的还是C语言和C&#xff0c;但是在那个时期嵌入式初…...

深度学习 - 梯度下降优化方法

梯度下降的基本概念 梯度下降&#xff08;Gradient Descent&#xff09;是一种用于优化机器学习模型参数的算法&#xff0c;其目的是最小化损失函数&#xff0c;从而提高模型的预测精度。梯度下降的核心思想是通过迭代地调整参数&#xff0c;沿着损失函数下降的方向前进&#…...

Steam下载游戏很慢?一个设置解决!

博主今天重装系统后&#xff0c;用steam下载发现巨慢 500MB&#xff0c;都要下载半小时。 平时下载软件&#xff0c;一般1分钟就搞定了&#xff0c;于是大致就知道&#xff0c;设置应该出问题了 于是修改了&#xff0c;如下设置之后&#xff0c;速度翻了10倍。 如下&#x…...

51单片机采用定时器T1的方式1的中断计数方式,外接开关K4按4次后,8只LED闪烁不停

1、功能描述 采用定时器T1的方式1的中断计数方式&#xff0c;外接开关K4按4次后&#xff0c;8只LED闪烁不停 2、实验原理 定时器原理:8051的定时器可以用于计数外部事件或执行内部定时操作。在本程序中&#xff0c;定时器1被设置为模式2&#xff0c;即8位自动重装载定时器模式…...

windows系统 flutter 开发环境配置

1、管理员运行powershell&#xff0c;安装&#xff1a;Chocolatey 工具&#xff0c;粘贴复制运行下列脚本: Chocolatey 官方安装文档 Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol [System.Net.ServicePointManage…...

【线性代数】SVDPCA

用最直观的方式告诉你&#xff1a;什么是主成分分析PCA_哔哩哔哩_bilibili 奇异值分解singular value decomposition&#xff0c;SVD principal component analysis,PCA 降维操作 pca就是降维后使得信息损失最小 投影在坐标轴上的点越分散&#xff0c;信息保留越多 pca的实现…...

1.Vue2使用ElementUI-初识及环境搭建

目录 1.下载nodejs v16.x 2.设置淘宝镜像源 3.安装脚手架 4.创建一个项目 5.项目修改 代码地址&#xff1a;source-code: 源码笔记 1.下载nodejs v16.x 下载地址&#xff1a;Node.js — Download Node.js 2.设置淘宝镜像源 npm config set registry https://registry.…...

OS复习笔记ch7-3

承接上文我们讲完了页式管理和段式管理&#xff0c;接下来让我们深入讲解一下快表和二级页表 快表 快表和计算机组成原理讲的Cache原理如出一辙。为了减少访存的次数&#xff0c;OS在访问页面的时候创建了快表&#xff08;Translation Lookaside Buffer &#xff0c;简称TLB&…...

MFC 教程-回车时窗口退出问题

【问题描述】 MFC窗口默认时&#xff0c;按回车窗口会退出 【原因分析】 默认调用OnOK() 【解决办法】 重写虚函PreTranslateMessage BOOL CTESTMFCDlg::PreTranslateMessage(MSG* pMsg) {// TODO: 在此添加专用代码和/或调用基类// 修改回车键的操作反应 if (pMsg->…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...