JavaFX: Java音乐播放读取歌词

news2024/11/17 1:54:51

JavaFX: Java音乐播放读取歌词

  • 1、lrc歌词文件
  • 2、解析lrc歌词
    • 2.1 读取每行歌词
    • 2.2 解析歌词时间标签Time-tag
    • 2.3 解析歌词标识标签ID-tags
    • 2.4 创建对象包含歌词相关信息
  • 3、播放显示歌词
  • ** 相关文献

JavaFX: Java音乐播放

1、lrc歌词文件

lrc歌词文件的扩展名

  • 1、标准格式: [分钟:秒.毫秒] 歌词
    注释:(如图1所示)中括号、冒号、点号全都要求英文输入状态;
  • 2、其他格式①:[分钟:秒] 歌词
  • 3、其他格式②:[分钟:秒:毫秒] 歌词,与标准格式相比,秒后边的点号被改成了冒号。

lrc歌词文本中含有两类标签:

  • 一是标识标签,其格式为“[标识名:值]”主要包含以下预定义的标签:
    [ar:歌手名]、[ti:歌曲名]、[al:专辑名]、[by:编辑者(指lrc歌词的制作人)]、[offset:时间补偿值] (其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的,但多数的MP3可能不会支持这种标签)。
  • 二是时间标签,形式为“[mm:ss]”或“[mm:ss.ff]”(分钟数:秒数.百分之一秒数),时间标签需位于某行歌词中的句首部分,一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。当歌曲播放到达某一时间点时,MP3就会寻找对应的时间标签并显示标签后面的歌词文本,这样就完成了“歌词同步”的功能。

■ 时间标签(Time-tag)
形式为"[mm:ss]"(分钟数:秒数)或"[mm:ss.ff]"。数字须为非负整数, 比如"[12:34.50]"是有效的,而"[0x0C:-34.50]"无效(但也有不太规范的歌词采用[00:-0.12]的方式表示负值以显示歌曲名,部分播放器是支持的)。 它可以位于某行歌词中的任意位置。一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK功能。另外,标签无须排序。

■ 标识标签(ID-tags)
其格式为"[标识名:值]"。大小写等价。以下是预定义的标签。
[ar:艺人名]
[ti:曲名]
[al:专辑名]
[by:编者(指编辑LRC歌词的人)]
[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。

2、解析lrc歌词

[by:天空的星空_SJ5q]
[ti:黄昏]
[ar:周传雄]
[al:忘记]
[by:]
[offset:0]
[00:31.86]
[00:35.19]过完整个夏天忧伤并没有好一些
[00:38.43]
[00:41.29]开车行驶在公路无际无边
[00:47.38]有离开自己的感觉
[00:50.91]
[00:53.64]唱不完一首歌
[00:57.07]
[00:59.75]疲倦还剩下黑眼圈
[01:03.12]
[01:05.92]感情的世界伤害在所难免
[01:12.03]黄昏再美终要黑夜
[01:15.65]
[01:18.64]依然记得从你口中
[01:21.69]说出再见坚决如铁
[01:25.16]昏暗中有种烈日灼身的错觉
[01:30.48]黄昏的地平线
[01:33.53]划出一句离别
[01:36.65]爱情进入永夜
[01:40.30]
[01:43.23]依然记得从你眼中
[01:46.22]滑落的泪伤心欲绝
[01:49.67]混乱中有种热泪烧伤的错觉
[01:55.10]黄昏的地平线
[01:58.17]割断幸福喜悦
[02:01.34]相爱已经幻灭
[02:05.48]
[02:19.87]唱不完一首歌
[02:22.96]
[02:25.89]疲倦还剩下黑眼圈
[02:29.39]
[02:32.11]感情的世界伤害在所难免
[02:38.14]黄昏再美终要黑夜
[02:42.39]
[02:44.73]依然记得从你口中
[02:47.79]说出再见坚决如铁
[02:51.26]昏暗中有种烈日灼身的错觉
[02:56.65]黄昏的地平线
[02:59.72]划出一句离别
[03:02.79]爱情进入永夜
[03:06.38]
[03:09.46]依然记得从你眼中
[03:12.38]滑落的泪伤心欲绝
[03:15.83]混乱中有种热泪烧伤的错觉
[03:21.26]黄昏的地平线
[03:24.34]割断幸福喜悦
[03:27.42]相爱已经幻灭
[03:31.49]
[03:58.73]依然记得从你口中
[04:01.60]说出再见坚决如铁
[04:05.18]昏暗中有种烈日灼身的错觉
[04:10.49]黄昏的地平线
[04:13.56]划出一句离别
[04:16.64]爱情进入永夜
[04:20.10]
[04:23.44]依然记得从你眼中
[04:26.26]滑落的泪伤心欲绝
[04:29.71]混乱中有种热泪烧伤的错觉
[04:35.10]黄昏的地平线
[04:38.17]割断幸福喜悦
[04:41.23]相爱已经幻灭

2.1 读取每行歌词

这里使用commons-ioorg.apache.commons.io.FileUtils

lineList = FileUtils.readLines(file, Charset.defaultCharset());

当然也可以自己实现:

InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);
BufferedReader bufferedReader = new BufferedReader(read);
String lineStr = null;
while ((lineStr = bufferedReader.readLine()) != null) {
    // ... ...
}
read.close();

2.2 解析歌词时间标签Time-tag

private static Map<Long, String> parseLongTime(String line) {
        String regex = "\\[(\\d{1,2}):(\\d{1,2}).(\\d{1,2})\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(line);
        Map<Long, String> map = null;
        while (matcher.find()) {
            map = new HashMap<Long, String>();
            // [00:35.19] ----对应---> [分钟:秒.毫秒]
            String min = matcher.group(1);
            String sec = matcher.group(2);
            String mill = matcher.group(3);
            long time = getLongTime(min, sec, mill + "0");

            String text = line.substring(matcher.end());
            map.put(time, text);
        }
        return map;
    }

    private static long getLongTime(String min, String sec, String mill) {
        int m = Integer.parseInt(min);
        int s = Integer.parseInt(sec);
        int ms = Integer.parseInt(mill);

        long time = m * 60 * 1000 + s * 1000 + ms;
        return time;
    }

2.3 解析歌词标识标签ID-tags

/**
     * [ar:艺人名]
     * [ti:曲名]
     * [al:专辑名]
     * [by:编者(指编辑LRC歌词的人)]
     * [offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
     */
    private static Map<String, String> parseIdTags(String line) {
        String regex = "\\[(ar|ti|al|by|offset):(.*)\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(line);
        Map<String, String> map = null;
        while (matcher.find()) {
            map = new HashMap<String, String>();
            String idTag = matcher.group(1);
            String idStr = matcher.group(2);

            XLog.i("matcher.groupCount() = " + matcher.groupCount() + "; idTag = " + idTag + "; idStr = " + idStr);
            map.put(idTag, idStr);
        }
        return map;
    }

2.4 创建对象包含歌词相关信息

重写toString()方法输出信息:
在这里插入图片描述

public class Lyric {
    /**
     * [ar:艺人名]
     * [ti:曲名]
     * [al:专辑名]
     * [by:编者(指编辑LRC歌词的人)]
     * [offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
     */
    private enum idTag {
        ar, ti, al, by, offset;
    }

    private String artist;
    private String title;
    private String album;
    private String bySomebody;
    private int offset;
    // [00:35.19] ----对应---> [分钟:秒.毫秒]
    private List<Map.Entry<Long, String>> lrcList;

    public Lyric() {
        lrcList = new ArrayList<>();
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public String getBySomebody() {
        return bySomebody;
    }

    public void setBySomebody(String bySomebody) {
        this.bySomebody = bySomebody;
    }

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public List<Map.Entry<Long, String>> getLrcList() {
        return lrcList;
    }

    public void setLrcList(List<Map.Entry<Long, String>> lrcList) {
        this.lrcList = lrcList;
    }

    public void addLrcMap(Map.Entry<Long, String> map) {
        lrcList.add(map);
    }

    public void set(Map.Entry<String, String> entry) {
        XLog.i(entry.getKey() + " : " + entry.getValue());
        String value = entry.getValue();
        if (value == null || value.trim().length() <= 0) {
            return;
        }

        String key = entry.getKey();
        if (idTag.ar.toString().equals(key)) {
            artist = value;
        } else if (idTag.ti.toString().equals(key)) {
            title = value;
        } else if (idTag.al.toString().equals(key)) {
            album = value;
        } else if (idTag.by.toString().equals(key)) {
            bySomebody = value;
        } else if (idTag.offset.toString().equals(key)) {
            offset = Integer.parseInt(value);
        }
    }

    @Override
    public String toString() {
        String str = "Lyric {" +
                "artist='" + artist + '\'' +
                ", title='" + title + '\'' +
                ", album='" + album + '\'' +
                ", bySomebody='" + bySomebody + '\'' +
                ", offset=" + offset +
                ", lrcList= {\n";
        StringBuffer sb = new StringBuffer(str);
        for (Map.Entry<Long, String> entry : lrcList) {
            sb.append("\t" + entry.toString() + "\n");
        }
        sb.append("\t}\n");
        return sb + "}";
    }
}

3、播放显示歌词

JavaFx中监听Service<Lyric>设置text控件

public static class LyricService extends Service<Lyric> {
    private static LyricService mInstance;
    private String mPath;

    private LyricService() {
    }

    public static LyricService getInstance() {
        if (mInstance == null) {
            synchronized (LyricService.class) {
                if (mInstance == null) {
                    mInstance = new LyricService();
                }
            }
        }
        return mInstance;
    }

    @Override
    protected Task<Lyric> createTask() {
        return new Task<>() {
            private Lyric mLyric;

            @Override
            protected Lyric call() {
                mLyric = LrcParser.parse(mPath);
                return mLyric;
            }
        };
    }

    public void startFindLyricService(AudioFile audioFile) {
        String ext = audioFile.getExt();
        String path = audioFile.getFile().getAbsolutePath();
        path = path.substring(0, path.length() - ext.length()) + "lrc";
        XLog.i(path);
        this.startFindLyricService(path);
    }

    public void startFindLyricService(String path) {
        mPath = path;
        reset();
        start();
    }
}

在这里插入图片描述

** 相关文献

Java使用正则表达式解析LRC歌词文件 https://www.cnblogs.com/wuqianling/p/5656761.html
lrc歌词文件格式 https://blog.51cto.com/u_13488278/3209754

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

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

相关文章

图像处理:Retinex算法

目录 前言 概念介绍 Retinex算法理论 单尺度Retinex&#xff08;SSR&#xff09; 多尺度Retinex&#xff08;MSR&#xff09; 多尺度自适应增益Retinex&#xff08;MSRCR&#xff09; Opencv实现Retinex算法 SSR算法 MCR算法 MSRCR算法 效果展示 总结 参考文章 前…

基频建模方法总结

基频F0建模方法 语音合成领域需要对基频进行建模&#xff0c;具体到文语转换TTS、语音转换VC、情感语音转换EVC领域等。 语音合成F0 包括文语转换&#xff0c;情感语音转换 TTEF&#xff1a;text-to-emotional-features synthesis EVC&#xff1a;emotional voice conversio…

这些你熟知的 app 和服务,都用上了人工智能

从微软在 Microsoft 365 服务中全面整合 GPT-4 能力 &#xff0c;让 PPT、Word 文档、Excel 表格的制作变成了「一句话的事」&#xff0c;到 Adobe 刚刚发布 Adobe Firefly模型集合&#xff0c;让图形设计、字体风格、视频渲染乃至 3D 建模的门槛显著降低——你我熟知的那些工…

idea的快捷键

一.idea的快捷键: 递进选择&#xff1a;ctrl w复制行&#xff1a;ctrl d删除行&#xff1a;ctrl y大小写切换&#xff1a;ctrl shift u展开/折叠&#xff1a;ctrl shift 减号/加号向前/向后&#xff1a;ctrl <— / —>Live Template(例如 输入psvm会自动打出mai…

华为OD机试题,用 Java 解【最远足迹】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不要…

Python实战项目:手势识别控制电脑音量

今天给大家带来一个OpenCV的实战小项目——手势识别控制电脑音量 先上个效果图&#xff1a; 通过大拇指和食指间的开合距离来调节电脑音量&#xff0c;即通过识别大拇指与食指这两个关键点之间的距离来控制电脑音量大小 技术交流 技术要学会分享、交流&#xff0c;不建议闭…

石英晶体振荡器【Multisim】【高频电子线路】

目录 一、实验目的与要求 二、实验仪器 三、实验内容与测试结果 1、观察输出波形&#xff0c;测量振荡频率和输出电压幅度 2、测量静态工作点的变化范围(IEQmin~IEQmax) 3、测量当静态工作点在上述范围时输出频率和输出电压的变化 4、测量负载变化对振荡频率和输出电压幅…

SpringCloud:微服务保护之初识Sentinel

1.初识Sentinel Sentinel是阿里巴巴开源的一款微服务流量控制组件。Sentinel官网 Sentinel具有以下特征&#xff1a; 丰富的应用场景&#xff1a;Sentinel承接了阿里巴巴近 10 年的双十一大促流量的核心场景&#xff0c;例如秒杀&#xff08;即突发流量控制在系统容量可以承受…

JavaEE阶段测试复习

文章全部内容在个人站点内的置顶文章中,访问密码:AIIT 小凯的宝库 模块三、面向对象 继承: a. 单继承:Java只支持单继承,即一个子类只能有一个直接父类。但子类可以间接地继承多个父类。 b. 构造方法与继承:在子类中可以通过super()关键字调用父类的构造方法。如果子类没…

探索深度学习中的计算图:PyTorch的动态图解析

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Windows系统的JDK安装与配置

1 选择JDK版本 以在Windows 64位平台上安装JDK 8版本为例。JDK 8 Windows版官网下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/#java8-windows 现在下载需要先注册并登录Oracle的账号。 2 安装 双击jdk安装包&#xff0c;进入安装程序页面直接选择…

freetype用法

freetype用法 文章目录 freetype用法0.实现1.变量定义2.lcd操作获取屏幕信息3.freetype初始化4.绘画 1.字形度量2.类1.FT 中的面向对象2.FT_Library 类3.FT_Face 类4 FT_Size 类5 FT_GlyphSlot 类 3.函数1.把一个字符码转换为一个字形索引FT_Get_Char_Index函数2.从 face 中装…

银行家算法--申请资源

银行家算法–申请资源 问题描述&#xff1a; 输入N个进程(N<100)&#xff0c;以及M类资源&#xff08;M<100&#xff09;&#xff0c;初始化各种资源的总数&#xff0c;T0时刻资源的分配情况。例如&#xff1a; 假定系统中有5个进程{P0&#xff0c;P1&#xff0c;P2&…

文字的显示

文字的显示 文章目录 文字的显示1.文字编码方式2.英文和汉字的点阵显示3.显示中文“中”和“A”show_font.c结果 1.文字编码方式 数字>代表什么->显示为什么 GBK国标拓展 下列代码用不同编码方式保存utf-8.c ansi.c #include <stdio.h>int main(int argc ,char *…

MyBaits的注解开发

为什么要用MyBaits注解开发&#xff1f; 前面介绍了MyBatis的基本用法、关联映射、动态SQL和缓存机制等知识&#xff0c;所有的配置都是基于XML文件完成的&#xff0c;但在实际开发中&#xff0c;大量的XML配置文件的编写是非常繁琐的&#xff0c;为此&#xff0c;MyBatis提供…

《Netty》从零开始学netty源码(五十一)之PoolArena的内存释放与扩容

目录 释放空间free()destroyChunk()freeChunk() 扩容reallocate()memoryCopy() 释放空间free() 当chunk使用完毕后需要释放&#xff0c;如果是池化chunk则需要放回池中供下次循环利用&#xff0c;如果是非池化的则直接释放对应的物理空间。 下面继续分析释放具体物理空间的des…

代码随想录算法训练营(总结)|动态规划总结篇

一、动态规划五部曲 确定dp及其下标的含义确定递推关系式初始化值确定遍历顺序验证 二、01背包问题 1. 基本理解 理解&#xff1a;所谓的01背包问题&#xff0c;其关键在于物品只能放入1次&#xff0c;不能够重复利用&#xff0c;因此称呼为01背包问题。与完全背包的区别在…

【深度学习】计算机视觉(十)——Faster RCNN(实践篇)

文章目录 写在最前面 step1&#xff1a;相关说明step2&#xff1a;Prerequisites安装要求tensorflow-gpu1. 安装和测试2. 错误提示① numpy版本② tensorflow版本③TensorRT step3&#xff1a;Installation1. Update your -arch2. Install the Python COCO API3. setup VOC4. D…

【VM服务管家】VM4.2平台SDK_6.4 结果获取类

目录 2.4.1 数据结果&#xff1a;通过流程输出或者模块输出获取数据结果的方法2.4.2 流程回调&#xff1a;某个流程运行开始与结束的回调方法 2.4.1 数据结果&#xff1a;通过流程输出或者模块输出获取数据结果的方法 描述 环境&#xff1a;VM4.2 VS2013及以上 现象&#xff…

从不自量力到 AI 助力,我如何翻译完一整本英文书

披露和声明&#xff1a; 本文提及的翻译作品系使用 AI 技术制作&#xff0c;并经人工调整&#xff0c;具体步骤如文中所述。本文在后期修改过程中使用 ChatGPT 辅助&#xff0c;目的为精简原版中的口语化表述。我的英文水平有限&#xff0c;翻译这本书主要是为了自我学习&…