Search code examples
javaaudiomp3jlayer

Specifying start/stop time in millisecs when playing MP3 using JLayer


I need to play a part of an MP3 file in my java code. I wish to do this via a function which accepts the start and stop time in millisecs.

JLayer contains a class called AdvancedPlayer which has a method that accepts the start and stop position in frames:

/**
 * Plays a range of MPEG audio frames
 * @param start The first frame to play
 * @param end       The last frame to play 
 * @return true if the last frame was played, or false if there are more frames.
 */
public boolean play(final int start, final int end) throws JavaLayerException
{
    boolean ret = true;
    int offset = start;
    while (offset-- > 0 && ret) ret = skipFrame();
    return play(end - start);
}

According to this, a frame lasts 26millisecs. However I need a finer degree of control than this, i.e. I may wish to play from 40millisecs to 50millisecs.

How can I do this? Do I need to convert the MP3 to .wav first?


Solution

  • The solution I used in the end was to first write the code to play a part of a wave file (i.e. from xxx ms to xxx ms) as I also need support for this file format. Here's the code for that:

    File soundFile = new File(this.audioFilePath);
    AudioInputStream originalAudioInputStream = AudioSystem.getAudioInputStream(soundFile);
    AudioFormat audioFormat = originalAudioInputStream.getFormat();
    
    float startInBytes = (startTimeinMs / 1000 * audioFormat.getSampleRate() * audioFormat.getFrameSize());
    float lengthInFrames = ((endTimeinMs - startTimeinMs) / 1000 * audioFormat.getSampleRate());
    
    originalAudioInputStream.skip((long) startInBytes);
    AudioInputStream partAudioInputStream = new AudioInputStream(originalAudioInputStream,
                    originalAudioInputStream.getFormat(), (long) lengthInFrames);
    
    // code to actually play the audio input stream here
    

    Once this was working I wrote this code to convert an MP3 to a temporary wave file (which I can then use with the above code) - this is using JLayer and MP3SPI. I did try simply performing the above directly on the converted audio stream without first writing out to a file but couldn't get it to work. I'm only using small MP3 files that convert/write out instantly so I'm happy with this solution.

    File soundFile = new File(this.inputFilePath);
    AudioInputStream mp3InputStream = AudioSystem.getAudioInputStream(soundFile);
    AudioFormat baseFormat = mp3InputStream.getFormat();
    AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,  baseFormat.getSampleRate(),  16, baseFormat.getChannels(), baseFormat.getChannels() * 2,  baseFormat.getSampleRate(), false);
    
    AudioInputStream convertedAudioInputStream =  AudioSystem.getAudioInputStream(decodedFormat, mp3InputStream);
    
    File outputFile = new File(this.outputFilePath);
    AudioSystem.write(convertedAudioInputStream, AudioFileFormat.Type.WAVE, outputFile);