
org.monte.media.mp3.MP3AudioInputStream Maven / Gradle / Ivy
/*
* @(#)MP3AudioInputStream.java 1.0.1 2011-03-18
*
* Copyright © 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.mp3;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
/**
* {@code AudioInputStream} adapter for {@link MP3ElementaryInputStream}.
*
* Unlike a regular audio input stream, an MP3 audio input stream can have a
* variable frame size and can change its encoding method in mid-stream.
* Therefore method getFormat can return different values for each frame,
* and mark/reset is not supported, and method getFrameLength can not return
* the total number of frames in the stream.
*
* @author Werner Randelshofer
* @version 1.0.1 2011-03-18 Method read(byte[],int,int) must not read multiple
* frames of different sizes.
*
1.0 2011-01-01 Created.
*/
public class MP3AudioInputStream extends AudioInputStream {
private MP3ElementaryInputStream in;
/** Creates an MP3AudioInputStream and reads the stream until the first
* frame is reached.
*
* @param file A File.
* @throws java.io.IOException if the file does not contain an MP3 elementary stream.
*/
public MP3AudioInputStream(File file) throws IOException {
this(new BufferedInputStream(new FileInputStream(file)));
}
/** Creates an MP3AudioInputStream and reads the stream until the first
* frame is reached.
*
* @param in An InputStream.
* @throws java.io.IOException if the stream does not contain an MP3 elementary stream.
*/
public MP3AudioInputStream(InputStream in) throws IOException {
// Feed superclass with nonsense - we override all methods anyway.
super(null, new AudioFormat(MP3ElementaryInputStream.MP3, 44100, 16, 2, 626, 44100f / 1152f, true), -1);
this.in = new MP3ElementaryInputStream(in);
if (this.in.getNextFrame() == null) {
throw new IOException("Stream is not an MP3 elementary stream");
}
}
@Override
public int available() throws IOException {
return in.available();
}
/** Returns the format of the next frame. Returns null if the stream
* is not positioned inside a frame.
*/
@Override
public AudioFormat getFormat() {
return in.getFormat();
}
/** Returns -1 because we don't know how many frames the stream has. */
@Override
public long getFrameLength() {
return -1;
}
@Override
public void close() throws IOException {
in.close();
}
/** Throws an IOException, because the frame size is greater than 1. */
@Override
public int read() throws IOException {
throw new IOException("cannot read a single byte if frame size > 1");
}
/** Reads some number of bytes from the audio input stream and stores them
* into the buffer array b. The number of bytes actually read is returned as
* an integer. This method blocks until input data is available, the end of
* the stream is detected, or an exception is thrown.
* This method will always read an integral number of frames. If the length
* of the array is not an integral number of frames, a maximum of
* {@code b.length - (b.length % frameSize)} bytes will be read.
*
* @return Returns the total number of bytes read into the buffer, or -1 if there is
* no more data because the end of the stream has been reached.
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (in.getFrame() == null && in.getNextFrame() == null) {
return -1;
}
if (in.getStreamPosition() != in.getFrame().getFrameOffset()) {
if (in.getNextFrame() == null) {
return -1;
}
}
int bytesRead = 0;
int frameSize = in.getFrame().getFrameSize();
while (len >= frameSize) {
in.readFully(b, off, frameSize);
len -= frameSize;
bytesRead += frameSize;
off += frameSize;
if (in.getNextFrame() == null
|| frameSize != in.getFrame().getFrameSize()) {
break;
}
}
return bytesRead;
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
@Override
public void mark(int readlimit) {
// can't do anything
}
@Override
public boolean markSupported() {
return false;
}
@Override
public void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
}