Java音乐播放程序,同步显示歌词(详细教程)

用java写一个音乐播放程序,同时显示歌词

目录

1.LRC歌词文件

2.解析歌词

 2.1对歌词进行解析

2.2对时间标签解析

2.3对歌词文件拆分

2.4歌词排序

 3.音乐接入

 4.整个程序

4.1  Yun2_4

4.2  Info

 4.3 Lyric


主题程序分为三部分{1.创建音乐播放窗口

                                  2.计时器

                                  3.对歌词进行处理

}

本程序运用了一个包,一个主程序,2个类进行处理歌词和时间

1.lrc歌词文件

在LRC歌词文本中,通常包含两类标签:时间标签和歌词文本标签。

  1. 时间标签:时间标签用于指定歌词的显示时间。它通常由方括号包围,并包含了歌词的起始时间和结束时间。时间标签的格式可以是分秒格式([mm:ss.xx])或者毫秒格式([mm:ss:xxx]),其中mm表示分钟,ss表示秒,xx或xxx表示毫秒。

示例:

[00:12.34]歌词内容
[01:23.45][02:34.56]歌词内容
 
 
  1. 歌词文本标签:歌词文本标签用于包含实际的歌词文本内容。它位于时间标签之后,直到下一个时间标签出现之前。歌词文本标签没有特定的格式要求,可以是任意文本内容。

示例:

 
 
[00:12.34]这是第一句歌词
[00:16.78]这是第二句歌词

通过使用这两类标签,LRC歌词文本可以指定每句歌词的显示时间,并且可以在指定的时间点显示相应的歌词内容。

2.解析歌词

这里以水星记歌词为例: 

[ti:水星记]
[ar:郭顶]
[al:飞行器的执行周期]
[by:]
[offset:0]
[00:00.00]水星记 - 郭顶
[00:06.50]词:郭顶
[00:13.00]曲:郭顶
[00:19.50]着迷于你眼睛
[00:21.81]
[00:23.21]银河有迹可循
[00:25.34]
[00:26.46]穿过时间的缝隙
[00:29.28]
[00:30.04]它依然真实地
[00:33.05]
[00:33.76]吸引我轨迹
[00:36.53]
[00:40.74]这瞬眼的光景
[00:43.25]
[00:44.27]最亲密的距离
[00:46.85]
[00:47.77]沿着你皮肤纹理 走
[00:51.68]过曲折手臂
[00:54.34]
[00:55.26]做个梦给你
[00:57.53]
[00:58.88]做个梦给你
[01:01.55]
[01:03.97]等到看你银色满际
[01:06.56]
[01:07.37]等到分不清季节更替
[01:11.89]
[01:13.24]才敢说沉溺
[01:16.38]
[01:20.28]还要多远才能进入你的心
[01:26.50]
[01:27.47]还要多久才能和你接近
[01:33.61]
[01:34.63]咫尺远近却
[01:37.08]无法靠近的那个人
[01:42.30]也等着和你相遇
[01:47.91]
[01:49.33]环游的行星
[01:52.05]
[01:52.57]怎么可以
[01:55.47]
[01:56.43]拥有你
[01:58.29]
[02:14.03]这瞬眼的光景
[02:16.57]
[02:17.42]最亲密的距离
[02:19.98]
[02:20.93]沿着你皮肤纹理
[02:24.61]走过曲折手臂
[02:27.66]
[02:28.36]做个梦给你
[02:30.78]
[02:31.88]做个梦给你
[02:34.92]
[02:36.89]等到看你银色满际
[02:40.59]等到分不清季节更替
[02:44.96]
[02:46.17]才敢说沉溺
[02:53.44]还要多远才能进入你的心
[02:59.70]
[03:00.63]还要多久才能和你接近
[03:06.78]
[03:07.73]咫尺远近却
[03:09.86]无法靠近的那个人
[03:15.43]也等着和你相遇
[03:20.94]
[03:22.13]环游的行星
[03:25.04]
[03:25.69]怎么可以
[03:29.38]拥有你
[03:35.83]
[04:05.52]还要多远才能进入你的心
[04:11.17]
[04:12.23]还要多久才能和你接近
[04:18.33]
[04:19.35]咫尺远近却无法靠近的那个人
[04:26.99]要怎么探寻
[04:30.62]要多么幸运
[04:33.84]才敢让你发觉你并不孤寂
[04:40.08]
[04:40.81]当我还可以再跟你飞行
[04:46.88]
[04:47.99]环游是无趣
[04:50.87]
[04:51.64]至少可以
[04:54.94]
[04:55.54]陪着你

 2.1对歌词进行解析

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            /*
             * 使用try-with-resources语句,创建一个BufferedReader对象并初始化它
             *BufferedReader将使用FileReader来读取指定文件的内容*/
            String[] arrStr = new String[100];
            int cnt = 0;//用于记录行数
            String line;
            while ((line = reader.readLine()) != null) {
                arrStr[cnt] = line;
                cnt++;
                /*
                 * 通过循环reader.readLine()方法会读取文件的下一行,并将其存储在line变量中
                 * 如果读取的行不为空,则将其存储在arrStr数组的当前索引位置,然后索引++*/
            }
            String[] arr = new String[cnt]; //创建一个新的数组,大小为歌词拥有的行数
            System.arraycopy(arrStr, 0, arr, 0, cnt);
            /*
             *  System.arraycopy复制方法,复制一个新的数组去掉多余的空
             *  arrStr表示是原数组,0表示起始位置,arr表示复制的目标数组,0表示起始位置,cnt表示复制元素的个数*/
            return arr;//返回值arr数组
        } catch (IOException e) {
            throw new RuntimeException(e);
        }//错误处理方法
    }

2.2对时间标签解析

 private long calSeconds(String str) {
        long minute;//分钟
        double second;//秒钟
        // 01:10.7
        String[] arrStr = str.split(":");//分割分秒
        minute = Long.parseLong(arrStr[0]);//使用Long.parseLong()方法将分钟部分的字符串转换为long类型的整数
        second = Double.parseDouble(arrStr[1]);//使用Double.parseDouble()方法将秒钟部分的字符串转换为double类型的小数
        return (long) ((minute * 60 + second) * 1000);
    }

2.3对歌词文件拆分

 public static Lyric[] lyricSplit(String str) {
        String[] strArr = str.split("]");
        int length = strArr.length - 1;// 使用split()方法将字符串str按照"]"进行拆分,得到一个字符串数组strArr。数组的长度减1得到变量length,表示有几个显示点。
        String lyric = strArr[length];//从strArr数组中获取最后一个元素,即歌词内容,赋值给变量lyric
        Lyric[] lyrics = new Lyric[length];//建一个长度为length的Lyric对象数组lyrics
        for (int i = 0; i < length; i++) {
            strArr[i] = strArr[i].substring(1);
            lyrics[i] = new Lyric(lyric, strArr[i]);
            /*用循环遍历strArr数组的前length个元素。
            在每次循环中,将strArr[i]的第一个字符去掉,得到去掉显示点的时间字符串,
            然后使用lyric和时间字符串创建一个Lyric对象,并将其赋值给lyrics数组的相应位置。
            * */
        }
        return lyrics;//返回lyrics数组,其中包含了拆分后的歌词数据
    }

2.4歌词排序

 public static void sort() {
        for (int i = 0; i < length - 1; i++) {//比较轮数
            for (int j = 0; j < length - i - 1; j++) {//比较次数
                if (lyrics[j].getTime() > lyrics[j + 1].getTime()) {//如果前面的歌词时间大于后面则调换顺序
                    Lyric lyric = lyrics[j];
                    lyrics[j] = lyrics[j + 1];
                    lyrics[j + 1] = lyric;
                }
            }
        }
    }

 3.音乐接入

 String musicFile = "D:\\作业素材\\郭顶 - 水星记.wav"; 
        Clip clip = null;
        try {
            File file = new File(musicFile);
            AudioInputStream audioStream = AudioSystem.getAudioInputStream(file);
            clip = AudioSystem.getClip();
            clip.open(audioStream);
        } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
            e.printStackTrace();
        }

String musicFile = "D:\\作业素材\\郭顶 - 水星记.wav"; 

歌曲文件的路径可以是绝对路径或者相对路径,音乐格式个别不能播放,wav。MP3

可以

 4.整个程序

4.1  Yun2_4

package yinyue;

import javax.sound.sampled.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Yun2_4 {
    static Lyric[] lyrics = new Lyric[100];// 定义一个数组用于存储歌词
    static int length = 0;// 实际歌词的元素个数

    public static void main(String[] args) {
        String cyanText="\u001B[36m";
        String yellowText="\u001B[33m";
        String resetText="\u001B[0m";

        System.out.println("\u001B[32m"+"欢迎使用音乐播放器!");
        System.out.println("\u001B[33m"+ "正在播放音乐:水星记 - 郭顶");//音乐名称

        String musicFile = "D:\\作业素材\\郭顶 - 水星记.wav"; //歌曲文件的路径,绝对路径或者相对路径,音乐格式个别不能播放

        Clip clip = null;//播放音乐的接口,初始化
        try {
            File file = new File(musicFile);
            AudioInputStream audioStream = AudioSystem.getAudioInputStream(file);//通过AudioSystem.getAudioInputStream(file)方法获取音频输入流audioStream
            clip = AudioSystem.getClip();//AudioSystem.getClip()用于获取一个clip对象
            clip.open(audioStream);//准备播放音乐
        } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
            e.printStackTrace();
        }//处理音乐不能播放的情况

        String[] arr = readFile("C:\\Users\\62626\\IdeaProjects\\HelloWorld\\untitled\\yinyue.txt");//歌词的路径,可以是相对路径或绝对路径

        // 加工数据
        processingData(arr);

        //歌词排序
        sort();

        clip.start();//打开音乐播放
        showLyric();//歌词显示
    }

    public static String[] readFile(String fileName) {
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            /*
             * 使用try-with-resources语句,创建一个BufferedReader对象并初始化它
             *BufferedReader将使用FileReader来读取指定文件的内容*/
            String[] arrStr = new String[100];
            int cnt = 0;//用于记录行数
            String line;
            while ((line = reader.readLine()) != null) {
                arrStr[cnt] = line;
                cnt++;
                /*
                 * 通过循环reader.readLine()方法会读取文件的下一行,并将其存储在line变量中
                 * 如果读取的行不为空,则将其存储在arrStr数组的当前索引位置,然后索引++*/
            }
            String[] arr = new String[cnt]; //创建一个新的数组,大小为歌词拥有的行数
            System.arraycopy(arrStr, 0, arr, 0, cnt);
            /*
             *  System.arraycopy复制方法,复制一个新的数组去掉多余的空
             *  arrStr表示是原数组,0表示起始位置,arr表示复制的目标数组,0表示起始位置,cnt表示复制元素的个数*/
            return arr;//返回值arr数组
        } catch (IOException e) {
            throw new RuntimeException(e);
        }//错误处理方法
    }

    /*try {
        Scanner sc = new Scanner(new File(fileName));
        String[] arrStr = new String[100];

        while (sc.hasNext()) {
            String line = sc.nextLine();


            arrStr[cnt] = line;
            cnt++;
        }
        String[] arr = new String[cnt];


        System.arraycopy(arrStr, 0, arr, 0, cnt);

        sc.close();
        return arr;

    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    }
}

*/
    public static void processingData(String[] arr) {
        for (String s : arr) {
            Lyric[] tmp = lyricSplit(s);//将当前字符串s拆分为Lyric对象数组tmp
            System.arraycopy(tmp, 0, lyrics, length, tmp.length);// System.arraycopy()方法将tmp数组中的元素复制到全局的lyrics数组
            length += tmp.length;//更新歌词长度
        }
    }

    // 歌词字符串拆分为Lyric对象数组,其中每个Lyric对象包含了歌词内容和对应的时间。
    public static Lyric[] lyricSplit(String str) {
        String[] strArr = str.split("]");
        int length = strArr.length - 1;// 使用split()方法将字符串str按照"]"进行拆分,得到一个字符串数组strArr。数组的长度减1得到变量length,表示有几个显示点。
        String lyric = strArr[length];//从strArr数组中获取最后一个元素,即歌词内容,赋值给变量lyric
        Lyric[] lyrics = new Lyric[length];//建一个长度为length的Lyric对象数组lyrics
        for (int i = 0; i < length; i++) {
            strArr[i] = strArr[i].substring(1);
            lyrics[i] = new Lyric(lyric, strArr[i]);
            /*用循环遍历strArr数组的前length个元素。
            在每次循环中,将strArr[i]的第一个字符去掉,得到去掉显示点的时间字符串,
            然后使用lyric和时间字符串创建一个Lyric对象,并将其赋值给lyrics数组的相应位置。
            * */
        }
        return lyrics;//返回lyrics数组,其中包含了拆分后的歌词数据
    }

    // 冒泡法排序歌词
    public static void sort() {
        for (int i = 0; i < length - 1; i++) {//比较轮数
            for (int j = 0; j < length - i - 1; j++) {//比较次数
                if (lyrics[j].getTime() > lyrics[j + 1].getTime()) {//如果前面的歌词时间大于后面则调换顺序
                    Lyric lyric = lyrics[j];
                    lyrics[j] = lyrics[j + 1];
                    lyrics[j + 1] = lyric;
                }
            }
        }
    }

    public static void showLyric() {
        long start = System.currentTimeMillis();//首先获取当前时间的毫秒数,并将其赋值给变量start作为起始时间
        int cnt = 0;//追踪已经展示的歌词数量
        System.out.println("\u001B[36m" + " ~~~~~~~~~~~~~~~~~~~~~~歌词信息~~~~~~~~~~~~~~~~~~~~~~~~~" + "\u001B[0m");

        while (cnt < length) {//只要cnt小于歌词数组的长度,就执行以下操作
            long current = System.currentTimeMillis() - start;//计算当前时间与起始时间的差值,并将其赋值给变量current,表示已经过去的时间
            if (current > lyrics[cnt].getTime()) {//如果current大于当前歌词对象的时间,说明该歌词应该被展示
                String lyric = lyrics[cnt].toString();//将当前歌词对象转换为字符串,并将其赋值给变量lyric
                String formattedLyric = "\u001B[35m" + "\u001B[1m" + "\u001B[4m" + lyric + "\u001B[0m"; // 设置歌词颜色为红色 加粗 下划线
                System.out.println("\u001B[34m" + "  * " + "\u001B[0m" + formattedLyric);

                cnt++;

            }
        }
        System.out.println("播放结束了~~~!");
    }

}

4.2  Info

package yinyue;


public class Info {
    private final String info;

    public Info(String str){
        info=str;
    }
//创建一个对象并初始化

    public String toString() {
        return info;
    }
}

 4.3 Lyric

package yinyue;

public class Lyric extends Info {
    private String timeStr;
    private long time;

    // 第一个参数是歌词,第二个参数是时间字符串
    public Lyric(String str, String timeStr) {
        super(str);
        this.timeStr = timeStr;
        time = calSeconds(timeStr);
    }

    public String getTimeStr() {
        return timeStr;
    }

    // 计算秒数 将 01:10.7 转为毫秒 (01*60+10.7)*1000
    private long calSeconds(String str) {
        long minute;//分钟
        double second;//秒钟
        // 01:10.7
        String[] arrStr = str.split(":");//分割分秒
        minute = Long.parseLong(arrStr[0]);//使用Long.parseLong()方法将分钟部分的字符串转换为long类型的整数
        second = Double.parseDouble(arrStr[1]);//使用Double.parseDouble()方法将秒钟部分的字符串转换为double类型的小数
        return (long) ((minute * 60 + second) * 1000);
    }


    public long getTime() {//获取时间
        return time;
    }

    public String toString() {
        return timeStr + " " + super.toString();//时间和字符串拼接后返回
    }
}

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值