2

I'm using JLayer to decode an MP3, and store it as PCM Data. However, no matter what MP3 file I use, I always get an ArrayIndexOutOfBoudnsException with the index 443. I have heard that this only happens with certain MP3s, but it is happening on every frame with every MP3 I play Here is my code:

private short[] getPCM(Header frameHeader, Bitstream bs) {
    short[] pcm = null;
    try {
        Decoder d = new Decoder();
        SampleBuffer buffer = (SampleBuffer) d.decodeFrame(frameHeader, bs);
        pcm = buffer.getBuffer();
    } catch (ArrayIndexOutOfBoundsException | DecoderException e) {
        System.err.println("JLayer, stap it");
    }
    return pcm;
}

And the method is called with this code:

while ((frameHeader = bs.readFrame()) != null) {
    short[] pcm = getPCM(frameHeader, bs);

    for(short i : pcm){
        try {
            os.write(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }       
}

Stack Trace:

java.lang.ArrayIndexOutOfBoundsException: 433
at javazoom.jl.decoder.Bitstream.get_bits(Unknown Source)
at javazoom.jl.decoder.LayerIIIDecoder.decode(Unknown Source)
at javazoom.jl.decoder.LayerIIIDecoder.decodeFrame(Unknown Source)
at javazoom.jl.decoder.Decoder.decodeFrame(Unknown Source)
at com.dentonposs.Canvas.getPCM(Canvas.java:70)
at com.dentonposs.Canvas.paintComponent(Canvas.java:48)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JComponent.paintChildren(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JLayeredPane.paint(Unknown Source)
at javax.swing.JComponent.paintChildren(Unknown Source)
at javax.swing.JComponent.paintToOffscreen(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
at javax.swing.RepaintManager.paint(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at java.awt.GraphicsCallback$PaintCallback.run(Unknown Source)
at sun.awt.SunGraphicsCallback.runOneComponent(Unknown Source)
at sun.awt.SunGraphicsCallback.runComponents(Unknown Source)
at java.awt.Container.paint(Unknown Source)
at java.awt.Window.paint(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.access$700(Unknown Source)
at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373

2 Answers2

1

From the looks of it, you are trying to have the decoder decoding the same frame over and over. That will not work, BitStream has internal state (a pointer to the next bit to read) that becomes invalid after decoding the frame. The exception indicates that it tries to read over the buffers capacity.

You must call decodeFrame() only once for each frame.

Edit: Your stacktrace does not show where you actually read the data. Since the exception is within the component paint method, I assume you read the frame on another thread or elsewhere.

Also, looking at your getPCM() method: That code will never work. You can not create a new decoder instance for every frame - that will simply not work because the decoder has a lot of internal state which is necessary and depends on the previous frame(s). You must create the decoder only once and re-use it for all frames.

Take a look at the source of javazoom.jl.player.Player - it shows a proper play loop in its play(int) method (its just a few lines long, but you must adhere to the call order shown there, otherwise you will destroy the proper state withing the decoder and the objects held by the decoder).

Durandal
  • 19,919
  • 4
  • 36
  • 70
  • But I only call decodeFrame once. Shouldn't `(frameHeader = bs.readFrame())` read the next frame? – gameboyprinter Nov 10 '13 at 20:20
  • @blendmaster345 Its hard to tell what you call in which order, since you do not clearly show where in the call stack the code fragments are executed (you probably find it obvious, but we can only see what you show us here). See my edit for some additional pointers. Adhere to the call order shown in the sample code provided with jlayer and your problems should vanish. – Durandal Nov 11 '13 at 17:14
  • That didn't fix the problem, but it helped with another one that came up, so thanks. – gameboyprinter Nov 12 '13 at 00:20
1

You need to call Bitstream.closeFrame() between each call to readFrame() - so

while ((frameHeader = bs.readFrame()) != null) {
    short[] pcm = getPCM(frameHeader, bs);

    bs.closeFrame();

    .. more ..
greg-449
  • 109,219
  • 232
  • 102
  • 145