简体   繁体   English

如何使用JLayer将歌词突出显示为mp3文件(如卡拉OK)

[英]How to have lyrics highlighted as mp3 file plays (like karaoke) using JLayer

I have created a media player in Java using JLayer, and it accepts mp3 files. 我使用JLayer创建了一个Java的媒体播放器,它接受mp3文件。 I also have the lyrics to a specific song appear once the user plays that song, but now I want to somehow highlight or change the text color of the lyrics as they are heard in the song (like karaoke). 一旦用户播放该歌曲,我也会出现特定歌曲的歌词,但现在我想以某种方式突出显示或更改歌曲中的歌词的文本颜色(如卡拉OK)。 I only need to do this for one song - and I'll have the lyrics already implemented in my program. 我只需要为一首歌做这个 - 我将在我的程序中实现歌词。 I have already searched for how to do this but can't seem to find exactly what I'm looking for. 我已经搜索了如何做到这一点,但似乎无法找到我正在寻找的确切内容。 Below I added the code to my class that plays the music file. 下面我将代码添加到播放音乐文件的班级。 Thanks in advanced! 提前致谢!

public class PlayMusic {

/**
 * Global variables. FileInputStream obtains input bytes from a file system
 * and reads streas of raw bytes. BufferedInputStream adds functionality
 * to the fis, and creates an internal buffer array.
 */
private String filename;
private Player player; 
private boolean canResume;
private boolean valid;
private int total;
private int stopped;
FileInputStream fis;
BufferedInputStream bis;


/**
 * Constructor the takes in the path of the mp3 file to be played.
 * @param filename - path of the mp3 file
 */
public PlayMusic(String filename) {
    this.filename = filename;
    this.canResume = false;
    this.valid = false;
    this.total = 0;
    this.stopped = 0;
    this.fis = null;
    this.bis = null;
}

/**
 * Function called to stop a song altogether as opposed to pausing.
 */
public void close() { 
    if (player != null) 
        player.close(); 
    stopped = 0;
    fis = null;
    bis = null;
    player = null;
    canResume = false;
    }


/**
 * Function called to pause a song. Fis.available() is a method that returns
 * the number of remaining bytes that can be read from the input stream.
 */
public void pause(){ 
    try {
        if (fis!=null)
            stopped = fis.available();
        if (player!= null)
            player.close();
        fis = null;
        bis = null;
        player = null;
        if(valid) 
            canResume = true;
    } catch (IOException e) {

    }


}

/**
 * Function called when we want to resume a song from where it left off
 * after being paused.
 */
public void resume()
{
    if(!canResume)
        return;
    if(play(total-stopped))
        canResume = false;
}

/**
 * Function called to play the song and keep track of where in the song the
 * user presses stop in order for the resume button to work properly. Fis.skip
 * skips over and discards pos bytes of data from fis.
 * @param pos - The position of the song in which we want to resume play
 * @return
 */
public boolean play(int pos) {
    valid = true;
    canResume = false;
    try {
        fis = new FileInputStream(filename);
        total = fis.available();
        if(pos> -1)
            fis.skip(pos);
        bis = new BufferedInputStream(fis);
        player = new Player(bis);

    }
    catch (Exception e) {
        System.out.println("Problem playing file " + filename);
        System.out.println(e);
    }



    /**
     * Run the play button in a new thread so the music plays in the background.
     */
    new Thread() {
        public void run() {
            try { player.play(); }
            catch (Exception e) { System.out.println(e); valid = false; }
        }
    }.start();


    return valid;

}

} }

You won't be able to detect what words are being sung in the song (with ease at least), but if you have the lyrics of the song in a file, and you have the sound file of the song, then to me it sounds like you could just add more info to that lyrics file to create a map of when the words of the lyrics are being sung in the song. 您将无法检测到歌曲中正在播放的单词(至少可以轻松),但如果您将歌曲的歌词放在文件中,并且您有该歌曲的声音文件,那么对我而言听起来你可以在该歌词文件中添加更多信息,以创建歌曲中歌词的歌词。

For example, if I was to do this with the song Jingle Bells , I may have a tab separated file that contains the lyrics, where one line is one word, with a begin and end time relative to the start of the song in milliseconds. 例如,如果我用歌曲Jingle Bells这样做,我可能有一个包含歌词的制表符分隔文件,其中一行是一个单词,相对于歌曲开头的开始和结束时间(以毫秒为单位)。

jingle 0 1000
bells 1001 1500
jingle 1501 2500
bells 2501 3000
... and so on

Edit for explaining how to code up keeping track of how long a song has been playing. 编辑以解释如何编码以跟踪歌曲播放的时间。

Two methods to create an instance variable called say, totalTimeSongHasBeenPlaying 创建实例变量的两种方法叫做totalTimeSongHasBeenPlaying

  1. I'm not sure how you have abstracted out playing your sound files, but say that you abstracted that out to a Sound class, then you could have three methods, sound.soundStarted , sound.soundStopped , sound.soundRestarted , then at the start of playing the sound you can call soundStarted which could grab a System.nanoTime or a System.currentTimeMillis and on soundStopped , you could grab it again and take the difference and add it to totalTimeSongHasBeenPlaying , and on soundRestart you could set totalTimeSongHasBeenPlaying to zero. 我不确定你是如何抽出你的声音文件,但是说你把它抽象出来的Sound类,那么你可以有三种方法, sound.soundStartedsound.soundStoppedsound.soundRestarted ,然后在开始播放声音你可以调用soundStarted ,它可以获取System.nanoTimeSystem.currentTimeMillis并且在soundStopped ,你可以再次抓取它并获取差异并将其添加到totalTimeSongHasBeenPlaying ,并且在soundRestart你可以将totalTimeSongHasBeenPlaying设置为零。

  2. Do some math against frame position the sound you are currently playing is versus how many frames is in a second for that file. 对帧位置进行一些数学计算,当前正在播放的声音与该文件的秒数相对应。 I don't know the exact libraries for JLayer , it's been a while since I used it, but that method should also tell you how far along in the file you are. 我不知道JLayer的确切库,自从我使用它以来已经有一段时间了,但是这个方法也应该告诉你你在文件中有多远。

After that, the Sound class could also then have a method such as currentWordBeingSung() , which looks at totalTimeSongHasBeenPlaying , and uses the lookup table you created off the lyrics file during construction and returns the specific word uniquely (may be duplicates). 之后,Sound类也可以有一个方法,如currentWordBeingSung() ,它查看totalTimeSongHasBeenPlaying ,并使用你在构造过程中创建的歌词文件的查找表,并唯一地返回特定的单词(可能是重复的)。 Your gui when you create it, say your JLyricsViewer , can hold an instance to your Sound object and you can use a SwingTimer to repaint it every 50 ms or so, where in your paintComponent method it looks at currentWordBeingSung() . 你创建它时你的gui,比如你的JLyricsViewer ,可以为你的Sound对象保存一个实例,你可以使用SwingTimer每50毫秒左右重新绘制一次,在paintComponent方法中它会查看currentWordBeingSung()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM