I was surprised to find that there was very little information on the internets on decoding MP3s for manipulation in Java, which is one of the things I would like to add to my project. After spending almost two weeks on this problem, I have finally cobbled together a solution, that while not perfect, gets close to a resolution. I would like to share my findings in the interest of helping any other struggling souls out there who are stuck. At this point, the main problem I am having is that certain large files are not being played in their entirety – only snippets are being played. I feel this is probably occurring somewhere in the logic of my array assignment; perhaps another pair of eyes could can spot something and post a tip in comments.
In any event, in order to get started, one needs a decoder class. There are two APIs that provide such classes, JLayer and Tritonus, which offers a plugin that can work with Java Sound. I used JLayer with MP3SPI as they have some documentation that serves as a good starting point.
Here is some code that exemplifies the process. This method takes a file name string in as a parameter and after defining an output format it is read through the decoder object and the resultant bytes are assigned to a ByteArrayOutputStream. After this, I send the stream as a byte array on return for further manipulation.
public byte[] testPlay(String filename) throws UnsupportedAudioFileException, IOException {
ByteArrayOutputStream f = new ByteArrayOutputStream();
File file = new File(filename);
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioFormat baseFormat = in.getFormat();
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(), 16,
baseFormat.getChannels(), baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), true);
DecodedMpegAudioInputStream decoder = new DecodedMpegAudioInputStream(decodedFormat, in);
try {
byte[] byteData = new byte[4];
int nBytesRead = 0;
int offset = 0;
while (nBytesRead != -1) {
nBytesRead = decoder.read(byteData, offset, byteData.length);
if (nBytesRead != -1) {
int numShorts = nBytesRead >> 1;
for (int j = 0; j < numShorts; j++) {
f.write(byteData[j]);
}
}
}
} catch (SynthException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
byte[] buffer = new byte[f.size()];
buffer = f.toByteArray();
f.close();
return buffer;
}
The above method is called like so and I proceed to assign the byte array as a short array so that I can feed it into the format that synthesis API I use, JSyn, prefers.
stream = (InputStream) (new FileInputStream(fileName));
String ext = fileName.substring(fileName.lastIndexOf('.')+1, fileName.length());
if (ext.equalsIgnoreCase("mp3") ) {
byte[] buffer = testPlay(fileName);
int j=0;
short shrtData[] = new short[buffer.length];
for (int i = 0; i < buffer.length ; i++) {
int sampled = ((int)(buffer[i])) & 0x00FF;
sampled += ((int)(buffer[i++])) << 8;
shrtData[j++] = (short) sampled;
}
if( shrtData != null ){
sampleTable.allocate( shrtData.length );
sampleTable.write( shrtData );
}
Hope this is helpful to someone out there…