大学作业-JAVA音频播放器

发布时间 2023-07-14 15:41:30作者: dianchaozhang

前言:

  播放音频 和 图形界面 都不是 java 擅长的领域。http 和 web 相关的领域才是 java 广泛应用的地方。

所以这个项目没啥意义,建议直接抄吧。

 

  项目使用的 java媒体框架jmf 是十几年前的老旧技术了,仅支持 wav 格式的音频文件播放。

jmf 官方网站:https://www.oracle.com/java/technologies/javase/java-media-framework.html

  参考资料:

大苏打

 

 

 

 

项目所需的 java 版本:1.8 或以上版本。maven 依赖如下:

<!-- lombok 非必要 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

<!-- Java Media Framework JAVA媒体框架 -->
<dependency>
    <groupId>javax.media</groupId>
    <artifactId>jmf</artifactId>
    <version>2.1.1e</version>
</dependency>

 

下面直接上代码:

 

项目启动入口:

/**
 * 项目主程序入口
 */
public class JAudioApp {

    /**
     * 项目主程序入口
     *
     * @param args
     */
    public static void main(String[] args) {

        JAudioGUI gui = new JAudioGUI(new JAudioPlayer());

        JAudioConst.AUDIO_FILE_PATH_LIST.forEach(x -> gui.addAudioFile(x));
    }

}

 

播放器实现类:

import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JOptionPane;

/**
 * 音频播放器
 */
public class JAudioPlayer {

    /** 播放器 */
    private Clip clip;

    /** 文件是否加载标志 */
    private volatile boolean isAudioLoad;

    /** 是否正在播放标志 */
    private volatile boolean isPlaying;

    /** 已播放时长 */
    private double playTime;

    /** 音频总时长 */
    private double audioFullTime;

    /** 歌词实体类 */
    private JAudioLyricDTO audioLyricDTO;

    public JAudioPlayer() {
        // 记录已播放时长
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isPlaying()) {
                    playTime += 0.01;
                }
            }
        }, 0, 10); // 每10毫秒刷新一次
    }

//    /**
//     * 测试播放的主方法
//     *
//     * @param args
//     */
//    public static void main(String[] args) {
//        String audioFilePath = JAudioConst.AUDIO_FILE_PATH_LIST.get(0);
//        System.out.println(audioFullTime(audioFilePath));
//        testPlay(audioFilePath);
//    }
//
//    private static void testPlay(String audioFilePath) {
//
//        // 睡眠 n 秒
//        Consumer<Integer> sleep = (seconds) -> {
//            try {
//                Thread.sleep(seconds * 1000L);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        };
//
//        /*
//         * 尝试播放一首歌,暂停后再播放,然后停止
//         */
//        JAudioPlayer player = new JAudioPlayer();
//        try {
//            player.load(audioFilePath);
//        } catch (Exception e) {
//            System.err.println("加载文件出错!" + audioFilePath + ", error message:" + e.getMessage());
//        }
//
//        player.play(); // 播放
//
//        sleep.accept(5); // 等待几秒
//        player.pause(); // 暂停
//
//        sleep.accept(5); // 等待几秒
//        player.play(); // 继续播放
//
//        sleep.accept(5); // 等待几秒
//        player.stop(); // 停止
//
//        player.play(); // stop() 后再调用此方法无效!
//    }

    /**
     * 获取音频总时长
     *
     * @param audioFilePath
     * @return
     */
    public static double audioFullTime(String audioFilePath) {
        try (InputStream in = JAudioUtils.loadFileAsStream(audioFilePath);
                AudioInputStream audioStream = AudioSystem.getAudioInputStream(in)) {

            AudioFormat audioFormat = audioStream.getFormat();

            return audioStream.getFrameLength() / audioFormat.getSampleRate();

        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "解析音频时长出错:" + audioFilePath //
                    + " error message:" + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "解析音频时长出错:" + audioFilePath //
                    + " error message:" + e.getMessage());
        }

        return 0;
    }

    /**
     * 加载文件,有问题时抛出异常
     *
     * @param audioFilePath
     * @throws Exception
     */
    public void load(String audioFilePath) throws Exception {
//        try {
//        } catch (UnsupportedAudioFileException e) {
//            throw new RuntimeException(e);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        } catch (LineUnavailableException e) {
//            throw new RuntimeException(e);
//        }

        // 先清空资源
        this.stop();

        // 重新加载资源
//        File audioFile = new File(audioFilePath);
//        AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);

//        AudioInputStream audioStream = AudioSystem //
//                .getAudioInputStream(JAudioPlayer.class.getResourceAsStream(audioFilePath));

        AudioInputStream audioStream = AudioSystem //
                .getAudioInputStream(JAudioUtils.loadFileAsStream(audioFilePath));

        AudioFormat audioFormat = audioStream.getFormat();

        // 获取音频总时长
        this.audioFullTime = audioStream.getFrameLength() / audioFormat.getSampleRate();

        DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
        this.clip = (Clip) AudioSystem.getLine(info);
        this.clip.open(audioStream);

        this.isAudioLoad = true;

        // 加载歌词文件

        String lyricFilePath = audioFilePath //
                .replace("audio/", "audioLyric/") //
                .replace(".wav", ".txt");
        try {
            this.audioLyricDTO = JAudioUtils.readAudioLyricFile(lyricFilePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 播放,检测到未加载文件时抛出异常。
     */
    public void play() {
        if (clip == null) {
            JOptionPane.showMessageDialog(null, "请先加载音乐文件!");
            throw new RuntimeException("请先加载音乐文件!");
        }

        clip.start();
        isPlaying = true;
    }

    /**
     * 暂停
     */
    public void pause() {
        if (clip != null && clip.isRunning()) {
            clip.stop();
        }
        isPlaying = false;
    }

    /**
     * 停止,停止后不能继续播放。除非重新加载文件。
     */
    public void stop() {
        if (clip != null && clip.isRunning()) {
            clip.stop();
            clip.close();
        }

        // restore
        isAudioLoad = false;
        isPlaying = false;
        playTime = 0.0;
        audioFullTime = 0.0;

        audioLyricDTO = null;
    }

    /**
     * 音频文件是否已加载
     * 
     * @return
     */
    public boolean isAudioLoad() {
        return isAudioLoad;
    }

    /**
     * 是否正在播放
     * 
     * @return
     */
    public boolean isPlaying() {
        return isPlaying;
    }

    /**
     * 获取已播放时间
     * 
     * @return
     */
    public double getPlayTime() {
        return playTime;
    }

    /**
     * 获取音频总时长
     * 
     * @return
     */
    public double getAudioFullTime() {
        return audioFullTime;
    }

    /**
     * 获取歌词实体类
     * 
     * @return
     */
    public JAudioLyricDTO getAudioLyricDTO() {
        return audioLyricDTO;
    }

}

 

图形界面实现:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextPane;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

public class JAudioGUI extends JFrame {

    /** 播放器 */
    private JAudioPlayer player;

    /** 全局背景 */
    private JLabel background;
    /** 播放按钮 */
    private JButton buttonPlay;
    /** 歌词模块 */
    private JTextPane paneLyric;

    /** 歌词模块 - 歌词文本对象 */
    private DefaultStyledDocument lyricDoc;
    /** 歌词模块 - 默认歌词格式 */
    private Style styleLyricDefault;
    /** 歌词模块 - 正在播放行的歌词格式 */
    private Style styleLyricFocus;

    /** 播放列表 */
    private JList<String> playList;
//    /** 进度条 */
//    private JLabel playTime;
    /** 进度条 */
    private JProgressBar progressBar;

    /** 播放列表中的音频文件 */
    private List<String> audioFilePathList = new ArrayList<>();
    /** 播放列表显示的名称 */
    private Vector<String> audioFileNames = new Vector<>();

//    public JAudioGUI() {
//        this(null);
//    }

    public JAudioGUI(JAudioPlayer player) {
        this.player = player;

        super.setTitle("播放器");
        super.setBounds(160, 100, 710, 430);
        super.setLayout(null);

        this.initBackground(); // 初始化全局背景
        this.initButtonPlay(); // 初始化播放按钮
        this.initPaneLyric(); // 初始化歌词模块
        this.initPlayList(); // 初始化播放列表
        this.initPlayProgress(); // 初始化进度条

        this.initGif(); // 初始化GIF

        super.setVisible(true);// 放在添加组件后面执行
        super.setDefaultCloseOperation(EXIT_ON_CLOSE);

        // 进度条 歌词滚动等
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (player == null) {
                    return;
                }

                // 进度条 start
                double playTimeDouble = player.getPlayTime();

                double progress = (playTimeDouble / player.getAudioFullTime()) * 1000;
                progressBar.setValue((int) progress);

                String playTimeStr = JAudioUtils.seconds2TimeStr((int) player.getPlayTime());
                progressBar.setString(playTimeStr);
                // 进度条 end

                // 设置歌词滚动显示 start
                JAudioLyricDTO lyricDTO = player.getAudioLyricDTO();
                if (lyricDTO == null || lyricDTO.getLyricTimeTextList().isEmpty()) {
                    return;
                }
                // 取出歌词实体类
                List<JAudioLyricTimeTextDTO> lyricTimeTextDTOList = lyricDTO.getLyricTimeTextList();
                // 取出需要展示的部分歌词
                List<JAudioLyricTimeTextDTO> lyric4Show = lyric4Show(lyricTimeTextDTOList, playTimeDouble);

                try {
                    lyricDoc.remove(0, lyricDoc.getLength()); // 清除现有内容
                    for (JAudioLyricTimeTextDTO timeTextDTO : lyric4Show) {
                        lyricDoc.insertString(lyricDoc.getLength(), timeTextDTO.getLyricText() + "\n",
                                timeTextDTO.isFocusOn() ? styleLyricFocus : styleLyricDefault);
                    }
//                    lyricDoc.setCharacterAttributes(10, 10, styleLyricFocus, true);
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
                // 设置歌词滚动显示 end
            }
        }, 0, 100); // 每 100毫秒(0.1秒)刷新一次
    }

    private List<JAudioLyricTimeTextDTO> lyric4Show(List<JAudioLyricTimeTextDTO> lyricTimeTextDTOList,
            double playTimeDouble) {

        lyricTimeTextDTOList.forEach(x -> x.setFocusOn(false)); // 重置 focusOn 的值

        List<JAudioLyricTimeTextDTO> dtoList = new ArrayList<>();

        // 找到正在播放的歌词所在的行
        int targetIndex = -99;
        for (int i = 0; i < lyricTimeTextDTOList.size(); i++) {
            JAudioLyricTimeTextDTO timeTextDTO = lyricTimeTextDTOList.get(i);
            double lyricTimeDouble = timeTextDTO.getLyricTimeDouble();

            if (lyricTimeDouble >= playTimeDouble) {
//            if (playTimeDouble - lyricTimeDouble <= 0.1) { // 提前 0.1 秒加载
                targetIndex = i;
                timeTextDTO.setFocusOn(true);
                break;
            }
        }

        // 取前2行 ~ 后3行,共6行数据
        int startIndex = targetIndex - 2;
        int endIndex = targetIndex + 3;

        for (int i = startIndex; i <= endIndex; i++) {

            if (i < 0 || i >= lyricTimeTextDTOList.size()) {
                dtoList.add(new JAudioLyricTimeTextDTO(null, ""));
                continue;
            }
            dtoList.add(lyricTimeTextDTOList.get(i));
        }

        return dtoList;
    }

    /**
     * 添加音频文件
     */
    public void addAudioFile(String filePath) {
        audioFilePathList.add(filePath);

        double audioFullTime = JAudioPlayer.audioFullTime(filePath);
        String audioFullTimeStr = JAudioUtils.seconds2TimeStr((int) audioFullTime);

        String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
        audioFileNames.add(fileName + "  " + audioFullTimeStr);
    }

    /**
     * 初始化全局背景
     */
    private void initBackground() {
        this.background = new JLabel();
//        background.setIcon(new ImageIcon(JAudioConst.IMAGE_BACKGROUND)); // 设置背景图片
        background.setIcon(JAudioUtils.readImageIcon(JAudioConst.IMAGE_BACKGROUND)); // 设置背景图片
        background.setBounds(0, 0, 700, 400); // 设置背景控件大小
        super.getLayeredPane().add(background, new Integer(Integer.MIN_VALUE)); // 背景图片控件置于最底层
        ((JPanel) super.getContentPane()).setOpaque(false); // 控件透明
    }

    /**
     * 初始化播放按钮
     */
    private void initButtonPlay() {
        this.buttonPlay = new JButton();
        super.add(buttonPlay);

        buttonPlay.setBounds(322, 335, 40, 40);
        buttonPlay.setIcon(JAudioUtils.readImageIcon(JAudioConst.IMAGE_PLAY)); // 设置图标
//        buttonPlay.setText("播放");
        buttonPlay.setOpaque(false); // 背景透明

        // 添加按钮响应
        buttonPlay.addActionListener(actionEvent -> {
            if (player == null || !player.isAudioLoad()) {
                return;
            }

            if (player.isPlaying()) {
                player.pause(); // 正在播放则暂停
                buttonPlay.setIcon(JAudioUtils.readImageIcon(JAudioConst.IMAGE_PLAY)); // 设置图标
            } else {
                player.play(); // 不在播放则播放
                buttonPlay.setIcon(JAudioUtils.readImageIcon(JAudioConst.IMAGE_PAUSE)); // 设置图标
            }
        });
    }

    /**
     * 初始化歌词模块
     */
    private void initPaneLyric() {
        // 初始化 歌词格式对象
        StyleContext styleContext = new StyleContext();
        this.styleLyricDefault = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
        StyleConstants.setForeground(styleLyricDefault, Color.WHITE);

        this.styleLyricFocus = styleContext.addStyle("styleLyricFocus", null);
        StyleConstants.setForeground(styleLyricFocus, Color.RED);
        StyleConstants.setBold(styleLyricFocus, true);

        // 初始化歌词模块
        this.lyricDoc = new DefaultStyledDocument();
        this.paneLyric = new JTextPane(lyricDoc);
        super.add(paneLyric);

        paneLyric.setBounds(20, 100, 200, 100);
        paneLyric.setForeground(Color.white);// 字体白色
        paneLyric.setEditable(false);
        paneLyric.setOpaque(false);// 背景透明
//        paneLyric.setText("芙蓉花又栖满了枝头 \n" + "奈何蝶难留\n" + "漂泊如江水向东流\n" + "望断门前隔岸的杨柳 \n");
        paneLyric.setText("------歌词------\n" //
                + "------歌词------\n" //
                + "------歌词------\n" //
                + "------歌词------\n" //
                + "------歌词------\n" //
                + "------歌词------" //
        );

//        try {
//            // 添加文本时设置默认的格式
//            lyricDoc.insertString(lyricDoc.getLength(), "line1: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line2: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line3: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line4: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line5: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line6: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line7: text text \n", styleLyricDefault);
//            lyricDoc.insertString(lyricDoc.getLength(), "line8: text text \n", styleLyricFocus);
//            lyricDoc.insertString(lyricDoc.getLength(), "line9: text text \n", styleLyricDefault);
//
//            // 修改已有文本的格式
//            lyricDoc.setCharacterAttributes(10, 10, styleLyricFocus, true);
//            // 删除文本
//            lyricDoc.remove(0, 3);
//        } catch (BadLocationException e) {
//            e.printStackTrace();
//        }

    }

    /**
     * 初始化播放列表
     */
    private void initPlayList() {
        this.playList = new JList(); // 创建播放列表
        super.add(playList); // 添加播放列表至窗口中

        playList.setBounds(500, 100, 150, 150); // 设置播放列表大小
        playList.setOpaque(false);// 设置播放列表透明
        playList.setBackground(new Color(0, 0, 0, 0));// 设置播放列表背景颜色
        playList.setForeground(Color.white);// 设置播放列表字体颜色

//        playFiles.forEach(
//                x -> vertor.add(x.getName() + "\t" + Math.floor(JAudioPlayer.audioFullTime(x.getAbsolutePath()))));
//
//        Vector<String> vertor = new Vector<>();
//        vertor.add("花海");
//        vertor.add("珊瑚海");
//        vertor.add("七里香");
//        vertor.add("浪漫手机");
//        playList.setListData(vertor);

        playList.setListData(audioFileNames);

        playList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent mouseEvent) {
                // 双击
                if (mouseEvent.getClickCount() == 2) {
                    int jlistIndex = playList.getSelectedIndex();

                    // 停止播放当前的音乐
                    player.stop();

                    // 加载双击选中的文件
                    String audioFilePath = audioFilePathList.get(jlistIndex);
                    try {
                        player.load(audioFilePath);
                    } catch (Exception e) {
                        JOptionPane.showMessageDialog(null, "加载音频文件出错:" + audioFilePath);

//                        JOptionPane.showMessageDialog(null, "加载音频文件出错:"
////                                + e.getMessage() + "\n"
//                                 + e.getStackTrace()
////                                + e.getCause()
//                        );
                    }

                    // 播放双击选中的文件
                    player.play();

                    // 更改播放按钮的图标
                    buttonPlay.setIcon(JAudioUtils.readImageIcon(JAudioConst.IMAGE_PAUSE)); // 设置图标
                }
            }
        });
    }

    /**
     * 初始化进度条
     */
    private void initPlayProgress() {
//        this.playTime = new JLabel(); // 创建播放进度条对象
//      playTime.setIcon(new ImageIcon(imageFilePath_timeFlag));
//      playTime.setBounds(0,324,0,3); // 设置播放进度条对象大小
//        super.add(playTime); // 添加播放进度条至窗口中

//        progressBar.setForeground(Color.BLACK);
//        progressBar.setStringPainted(true);
//        progressBar.setOpaque(false);
//        progressBar.setBounds(73, 103, 434, 24);
//        this.getContentPane().add(progressBar);

        this.progressBar = new JProgressBar();
        super.add(progressBar);

        progressBar.setMaximum(1000);
        progressBar.setMinimum(0);
        progressBar.setStringPainted(true);
        progressBar.setIndeterminate(false);
        progressBar.setBounds(0, 314, 700, 20);
        progressBar.setOpaque(false); // 透明
//        progressBar.setPreferredSize(new Dimension(780, 30));

//        progressBar.setValue(50); // 进度百分值
//        progressBar.setString("02:19"); // 显示文本
    }

    private void initGif() {
        // 创建面板
        JPanel jPanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                graphics.setColor(getBackground());
                graphics.fillRect(0, 0, super.getWidth(), super.getHeight());
            }
        };
        jPanel.setBounds(240, 100, 200, 100);
        jPanel.setLayout(new GridBagLayout());
        jPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
        jPanel.setOpaque(false);
        jPanel.setBackground(new Color(0, 0, 0, 150));

        JLabel loadBar = new JLabel(JAudioUtils.readImageIcon(JAudioConst.GIF_PATH));
        loadBar.setHorizontalAlignment(JLabel.CENTER);
        loadBar.setVerticalAlignment(JLabel.CENTER);
        jPanel.add(loadBar);

        this.background.add(jPanel);
    }

}

 

项目的工具类:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

/**
 * 工具类
 */
public class JAudioUtils {

    /**
     * 将秒数转换为时分秒 HH:mm:ss
     *
     * @param seconds
     * @return
     */
    public static String seconds2TimeStr(int seconds) {
        if (seconds < 0) {
            return "";
        }

        // 计算
        int second = seconds % 60;
        int minutes = seconds / 60;
        int minute = minutes % 60;
        int hours = minutes / 60;

        // 拼接
        StringBuilder sb = new StringBuilder();
        if (hours > 0) {
            sb.append(hours).append(":");
        }

        sb.append(String.format("%02d", minute)).append(":");
        sb.append(String.format("%02d", second));

        return sb.toString();
    }

    /**
     * 读取歌词文件,并封装成实体类
     *
     * @param lyricFilePath
     * @return
     */
    public static JAudioLyricDTO readAudioLyricFile(String lyricFilePath) {
        List<String> lines = readTextFile(lyricFilePath);
        return analyzeLyricLines(lines);
    }

    /**
     * 按行读取文本文件
     *
     * @param textFilePath
     * @return
     */
    public static List<String> readTextFile(String textFilePath) {
        List<String> lines = new ArrayList<>();

        // 按行读取歌词文件
        try (InputStreamReader isr = new InputStreamReader(JAudioUtils.loadFileAsStream(textFilePath), "UTF-8");
                BufferedReader reader = new BufferedReader(isr)) {
            String line = null;

            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "读取文本文件出错:" + textFilePath //
                    + " error message:" + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "读取文本文件出错:" + textFilePath //
                    + " error message:" + e.getMessage());
        }

        return lines;
    }

    /**
     * 解析歌词
     *
     * @param lyricLines
     * @return
     */
    public static JAudioLyricDTO analyzeLyricLines(List<String> lyricLines) {
//        [ti:千里之外]
//        [ar:周杰伦/费玉清]
//        [al:依然范特西]
//        [by:]
//        [offset:0]
//        [00:00.00]千里之外 - 周杰伦 (Jay Chou)/费玉清 (Yu-Ching Fei)
//        [00:04.91]词:方文山
//        [00:09.82]曲:周杰伦
//        [00:14.73]编曲:林迈可
//        [00:19.64]周:
//        [00:24.66]屋檐如悬崖
//        [00:26.75]风铃如沧海
//        [00:28.76]我等燕归来

        // 正则表达式,用于提取 歌词内容
        String regexTitle = "^\\[ti:(.*)\\]$";
        String regexAuthor = "^\\[ar:(.*)\\]$";
        String regexAlbum = "^\\[al:(.*)\\]$";
        String regexBy = "^\\[by:(.*)\\]$";
        String regexOffset = "^\\[offset:(.*)\\]$";
        String regexLyricTimeText = "^\\[(\\d{2}:\\d{2}.\\d{2})\\](.*)$";

        // 用于提取 title author 等字段的 function
        BiFunction<String, List<String>, String> matchFunc = (regex, strList) -> {
            Pattern pattern = Pattern.compile(regex);
            for (String str : strList) {
                Matcher matcher = pattern.matcher(str);
                if (matcher.matches()) {
                    return matcher.group(1);
                }
            }
            return "";
        };

        // 用于提取 歌词时间 歌词内容的 function
        BiFunction<String, List<String>, List<JAudioLyricTimeTextDTO>> matchFunc2 = (regex, strList) -> {
            List<JAudioLyricTimeTextDTO> lyricTimeTextList = new ArrayList<>();
            Pattern pattern = Pattern.compile(regex);
            for (String str : strList) {
                Matcher matcher = pattern.matcher(str);
                if (matcher.matches()) {
                    String lyricTime = matcher.group(1);
                    String lyricText = matcher.group(2);
                    lyricTimeTextList.add(new JAudioLyricTimeTextDTO(lyricTime, lyricText));
                }
            }
            return lyricTimeTextList;
        };

        // 提取歌词文件的内容,并封装对象
        JAudioLyricDTO dto = new JAudioLyricDTO();
        dto.setTitle(matchFunc.apply(regexTitle, lyricLines));
        dto.setAuthor(matchFunc.apply(regexAuthor, lyricLines));
        dto.setAlbum(matchFunc.apply(regexAlbum, lyricLines));
        dto.setBy(matchFunc.apply(regexBy, lyricLines));
        dto.setOffset(matchFunc.apply(regexOffset, lyricLines));
        dto.setLyricTimeTextList(matchFunc2.apply(regexLyricTimeText, lyricLines));

        // 将歌词所在时间 转换为 double
        dto.getLyricTimeTextList().forEach(x -> {

            // [01:13.24]我送你离开千里之外你无声黑白
            String lyricTimeStr = x.getLyricTime();
            int minutes = Integer.valueOf(lyricTimeStr.substring(0, 2));
            double seconds = Double.valueOf(lyricTimeStr.substring(3));

            double lyricTimeDouble = minutes * 60 + seconds;
            x.setLyricTimeDouble(lyricTimeDouble);
        });

        // 修正歌词后期不准的问题,根据百分比,修正 0-2 秒
//        double lastLyricTimeDouble = // 最后一句歌词
//                dto.getLyricTimeTextList().get(dto.getLyricTimeTextList().size() - 1).getLyricTimeDouble();

//        dto.getLyricTimeTextList().forEach(x -> {
////            double fixValue = x.getLyricTimeDouble() / lastLyricTimeDouble * 1.5;
//            x.setLyricTimeDouble(x.getLyricTimeDouble() * 0.95);
//        });

        return dto;
    }

    /**
     * 通过流的方式读取 image文件
     * 
     * @param relativePath
     * @return
     */
    public static ImageIcon readImageIcon(String relativePath) {
//        BufferedImage image = ImageIO.read(JAudioUtils.class.getResourceAsStream(relativePath));
        return new ImageIcon(JAudioUtils.class.getResource(relativePath));
    }

    /**
     * 将文件加载为 BufferedInputStream <br>
     * 注意本方法不会关闭流
     * 
     * @param relativePath
     * @return
     */
    public static BufferedInputStream loadFileAsStream(String relativePath) {
        return new BufferedInputStream(JAudioUtils.class.getResourceAsStream(relativePath));
    }

    public static void main(String[] args) {
//        System.out.println(seconds2TimeStr(261));
//        System.out.println(seconds2TimeStr(3861));
//        testRegex();

        JAudioLyricDTO dto = readAudioLyricFile("./src/main/resources/audioLyric/千里之外.txt");
        System.out.println(dto);
    }

//    private static void testRegex() {
//        String regexTitle = "^\\[ti:(.*)\\]$";
//        String regexAuthor = "^\\[ar:(.*)\\]$";
//        String regexAlbum = "^\\[al:(.*)\\]$";
//        String regexBy = "^\\[by:(.*)\\]$";
//        String regexOffset = "^\\[offset:(.*)\\]$";
//        String regexLyric = "^\\[(\\d{2}:\\d{2}.\\d{2})\\](.*)$";
//
//        String title = "[ti:千里之外]";
//        String author = "[ar:周杰伦/费玉清]";
//        String album = "[al:依然范特西]";
//        String by = "[by:]";
//        String offset = "[offset:0]";
//        String lyric = "[00:00.00]千里之外 - 周杰伦 (Jay Chou)/费玉清 (Yu-Ching Fei)";
//
//        Matcher matcher = Pattern.compile(regexLyric).matcher(lyric);
//        System.out.println(matcher.matches());
//        System.out.println(matcher.group(1));
//        System.out.println(matcher.group(2));
//    }

}

 

项目的常量类:

import java.util.Arrays;
import java.util.List;

/**
 * 常量类
 */
public interface JAudioConst {

    /** 默认的音频文件 */
    List<String> AUDIO_FILE_PATH_LIST = Arrays.asList( //
            "audio/千里之外.wav", //
            "audio/晴天.wav", //
            "audio/稻香.wav", //
            "audio/简单爱.wav", //
            "audio/花海.wav", //
            "audio/青花瓷.wav" //
    );

    /** 图片路径:背景图 */
    String IMAGE_BACKGROUND = "image/background_pink.jpg";

    /** 图片路径:播放按钮 */
    String IMAGE_PLAY = "image/play.png";

    /** 图片路径:播放按钮 */
    String IMAGE_PAUSE = "image/pause.png";

//    /** 图片路径:进度条 */
//    String IMAGE_PROGRESS = "image/progress.png";

    /** gif 动图路径 */
//    String GIF_PATH = "image/gif_flower.gif";
    String GIF_PATH = "image/gif_bongoCat.gif";

}

 

项目的实体类:

import java.util.List;

import lombok.Data;

/**
 * 歌词实体类
 */
@Data
public class JAudioLyricDTO {

    private String title; // 标题
    private String author; // 作者
    private String album; // 专辑
    private String by; // 这不知道是啥
    private String offset; // 这不知道是啥

    // 歌词 时间 和 文字
    private List<JAudioLyricTimeTextDTO> lyricTimeTextList;

}
import lombok.Data;

/**
 * 单句歌词实体类
 */
@Data
public class JAudioLyricTimeTextDTO {

    private String lyricTime;
    private String lyricText;

    private double lyricTimeDouble; // 歌词所在时间转换为 double 方便计算
    private boolean focusOn; // 是否正在播放本句

    public JAudioLyricTimeTextDTO(String lyricTime, String lyricText) {
        this.lyricTime = lyricTime;
        this.lyricText = lyricText;
    }

}