added Vorbis decoder, replaced own mp3 file reader with the default one

This commit is contained in:
Raybz@Raybz 2013-06-14 12:30:07 +02:00
parent 6c393ef77a
commit b44363990d
7 changed files with 217 additions and 183 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -34,6 +34,27 @@
<scope>system</scope>
<systemPath>${basedir}/lib/mp3spi1.9.5.jar</systemPath>
</dependency>
<dependency>
<groupId>org.tritonus</groupId>
<artifactId>jorbis</artifactId>
<version>0.3.6</version>
<scope>system</scope>
<systemPath>${basedir}/lib/tritonus_jorbis-0.3.6.jar</systemPath>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jogg</artifactId>
<version>0.0.7</version>
<scope>system</scope>
<systemPath>${basedir}/lib/jogg-0.0.7.jar</systemPath>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jorbis</artifactId>
<version>0.0.15</version>
<scope>system</scope>
<systemPath>${basedir}/lib/jorbis-0.0.15.jar</systemPath>
</dependency>
<dependency>
<groupId>org.tritonus</groupId>
<artifactId>share</artifactId>

View File

@ -26,7 +26,7 @@ import java.io.InputStream;
*/
public enum DecoderFactory {
WAVE, MP3;
WAVE, MP3, OGG;
/**
* Creates a decoder for the selected codec.
@ -36,10 +36,12 @@ public enum DecoderFactory {
*/
public Decoder create(InputStream is) throws Exception {
switch (this) {
case WAVE:
return new WaveDecoder(is);
case MP3:
return new MP3Decoder(is);
case OGG:
return new VorbisDecoder(is);
case WAVE:
return new WaveDecoder(is);
default: //impossible
return null;
}
@ -63,6 +65,8 @@ public enum DecoderFactory {
switch (extension.toUpperCase()) {
case "MP3":
return MP3.create(new FileInputStream(file));
case "OGG":
return OGG.create(new FileInputStream(file));
case "WAV":
case "WAVE":
return WAVE.create(new FileInputStream(file));

View File

@ -25,22 +25,11 @@ package org.wyrez.audio.decoder;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.HashMap;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Header;
import javazoom.spi.mpeg.sampled.file.MpegAudioFileFormat;
import javazoom.spi.mpeg.sampled.file.MpegAudioFormat;
import javazoom.spi.mpeg.sampled.file.MpegEncoding;
import javazoom.spi.mpeg.sampled.file.MpegFileFormatType;
import org.tritonus.share.TDebug;
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;
import org.tritonus.share.sampled.FloatSampleBuffer;
import org.tritonus.share.sampled.file.TAudioFileReader;
/**
* A decoder for MP3 files with a maximum of 2 channels.
@ -64,7 +53,7 @@ public class MP3Decoder implements Decoder {
*/
public MP3Decoder(InputStream stream) throws Exception {
InputStream in = new BufferedInputStream(stream, 1024 * 1024);
this.in = new MP3AudioFileReader().getAudioInputStream(in);
this.in = new MpegAudioFileReader().getAudioInputStream(in);
AudioFormat baseFormat = this.in.getFormat();
this.channels = baseFormat.getChannels();
if (channels > 2) {
@ -195,171 +184,4 @@ public class MP3Decoder implements Decoder {
public float getSamplingRate() {
return samplingRate;
}
class MP3AudioFileReader extends TAudioFileReader {
public static final int INITAL_READ_LENGTH = 128000;
private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
private final AudioFormat.Encoding[][] sm_aEncodings = {
{MpegEncoding.MPEG2L1, MpegEncoding.MPEG2L2, MpegEncoding.MPEG2L3},
{MpegEncoding.MPEG1L1, MpegEncoding.MPEG1L2, MpegEncoding.MPEG1L3},
{MpegEncoding.MPEG2DOT5L1, MpegEncoding.MPEG2DOT5L2,
MpegEncoding.MPEG2DOT5L3},};
protected MP3AudioFileReader() {
super(MARK_LIMIT, true);
}
@Override
protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long mediaLength)
throws UnsupportedAudioFileException, IOException {
HashMap aff_properties = new HashMap();
HashMap af_properties = new HashMap();
int mLength = (int) mediaLength;
int size = inputStream.available();
PushbackInputStream pis = new PushbackInputStream(inputStream, MARK_LIMIT);
byte head[] = new byte[22];
pis.read(head);
// Check for WAV, AU, and AIFF, Ogg Vorbis, Flac, MAC file formats.
// Next check for Shoutcast (supported) and OGG (unsupported) streams.
if ((head[0] == 'R') && (head[1] == 'I') && (head[2] == 'F')
&& (head[3] == 'F') && (head[8] == 'W') && (head[9] == 'A')
&& (head[10] == 'V') && (head[11] == 'E')) {
int isPCM = ((head[21] << 8) & 0x0000FF00) | ((head[20]) & 0x00000FF);
throw new UnsupportedAudioFileException("WAV PCM stream found");
} else if ((head[0] == '.') && (head[1] == 's') && (head[2] == 'n')
&& (head[3] == 'd')) {
throw new UnsupportedAudioFileException("AU stream found");
} else if ((head[0] == 'F') && (head[1] == 'O') && (head[2] == 'R')
&& (head[3] == 'M') && (head[8] == 'A') && (head[9] == 'I')
&& (head[10] == 'F') && (head[11] == 'F')) {
throw new UnsupportedAudioFileException("AIFF stream found");
} else if (((head[0] == 'M') | (head[0] == 'm'))
&& ((head[1] == 'A') | (head[1] == 'a'))
&& ((head[2] == 'C') | (head[2] == 'c'))) {
throw new UnsupportedAudioFileException("APE stream found");
} else if (((head[0] == 'F') | (head[0] == 'f'))
&& ((head[1] == 'L') | (head[1] == 'l'))
&& ((head[2] == 'A') | (head[2] == 'a'))
&& ((head[3] == 'C') | (head[3] == 'c'))) {
throw new UnsupportedAudioFileException("FLAC stream found");
} // Shoutcast stream ?
else if (((head[0] == 'I') | (head[0] == 'i'))
&& ((head[1] == 'C') | (head[1] == 'c'))
&& ((head[2] == 'Y') | (head[2] == 'y'))) {
pis.unread(head);
// Load shoutcast meta data.
} // Ogg stream ?
else if (((head[0] == 'O') | (head[0] == 'o'))
&& ((head[1] == 'G') | (head[1] == 'g'))
&& ((head[2] == 'G') | (head[2] == 'g'))) {
throw new UnsupportedAudioFileException("Ogg stream found");
} // No, so pushback.
else {
pis.unread(head);
}
// MPEG header info.
int nVersion = AudioSystem.NOT_SPECIFIED;
int nLayer = AudioSystem.NOT_SPECIFIED;
// int nSFIndex = AudioSystem.NOT_SPECIFIED;
int nMode = AudioSystem.NOT_SPECIFIED;
int FrameSize = AudioSystem.NOT_SPECIFIED;
// int nFrameSize = AudioSystem.NOT_SPECIFIED;
int nFrequency = AudioSystem.NOT_SPECIFIED;
int nTotalFrames = AudioSystem.NOT_SPECIFIED;
float FrameRate = AudioSystem.NOT_SPECIFIED;
int BitRate = AudioSystem.NOT_SPECIFIED;
int nChannels = AudioSystem.NOT_SPECIFIED;
int nHeader = AudioSystem.NOT_SPECIFIED;
int nTotalMS = AudioSystem.NOT_SPECIFIED;
boolean nVBR = false;
AudioFormat.Encoding encoding = null;
try {
Bitstream m_bitstream = new Bitstream(pis);
aff_properties.put("mp3.header.pos",
new Integer(m_bitstream.header_pos()));
Header m_header = m_bitstream.readFrame();
// nVersion = 0 => MPEG2-LSF (Including MPEG2.5), nVersion = 1 => MPEG1
nVersion = m_header.version();
if (nVersion == 2) {
aff_properties.put("mp3.version.mpeg", Float.toString(2.5f));
} else {
aff_properties.put("mp3.version.mpeg",
Integer.toString(2 - nVersion));
}
// nLayer = 1,2,3
nLayer = m_header.layer();
aff_properties.put("mp3.version.layer", Integer.toString(nLayer));
// nSFIndex = m_header.sample_frequency();
nMode = m_header.mode();
aff_properties.put("mp3.mode", new Integer(nMode));
nChannels = nMode == 3 ? 1 : 2;
aff_properties.put("mp3.channels", new Integer(nChannels));
nVBR = m_header.vbr();
af_properties.put("vbr", new Boolean(nVBR));
aff_properties.put("mp3.vbr", new Boolean(nVBR));
aff_properties.put("mp3.vbr.scale", new Integer(m_header.vbr_scale()));
FrameSize = m_header.calculate_framesize();
aff_properties.put("mp3.framesize.bytes", new Integer(FrameSize));
if (FrameSize < 0) {
throw new UnsupportedAudioFileException("Invalid FrameSize : " + FrameSize);
}
nFrequency = m_header.frequency();
aff_properties.put("mp3.frequency.hz", new Integer(nFrequency));
FrameRate = (float) ((1.0 / (m_header.ms_per_frame())) * 1000.0);
aff_properties.put("mp3.framerate.fps", new Float(FrameRate));
if (FrameRate < 0) {
throw new UnsupportedAudioFileException("Invalid FrameRate : " + FrameRate);
}
if (mLength != AudioSystem.NOT_SPECIFIED) {
aff_properties.put("mp3.length.bytes", new Integer(mLength));
nTotalFrames = m_header.max_number_of_frames(mLength);
aff_properties.put("mp3.length.frames", new Integer(nTotalFrames));
}
BitRate = m_header.bitrate();
af_properties.put("bitrate", new Integer(BitRate));
aff_properties.put("mp3.bitrate.nominal.bps", new Integer(BitRate));
nHeader = m_header.getSyncHeader();
encoding = sm_aEncodings[nVersion][nLayer - 1];
aff_properties.put("mp3.version.encoding", encoding.toString());
if (mLength != AudioSystem.NOT_SPECIFIED) {
nTotalMS = Math.round(m_header.total_ms(mLength));
aff_properties.put("duration", new Long((long) nTotalMS * 1000L));
}
aff_properties.put("mp3.copyright", new Boolean(m_header.copyright()));
aff_properties.put("mp3.original", new Boolean(m_header.original()));
aff_properties.put("mp3.crc", new Boolean(m_header.checksums()));
aff_properties.put("mp3.padding", new Boolean(m_header.padding()));
InputStream id3v2 = m_bitstream.getRawID3v2();
if (id3v2 != null) {
aff_properties.put("mp3.id3tag.v2", id3v2);
}
if (TDebug.TraceAudioFileReader) {
TDebug.out(m_header.toString());
}
} catch (Exception e) {
throw new UnsupportedAudioFileException("not a MPEG stream:"
+ e.getMessage());
}
// Deeper checks ?
int cVersion = (nHeader >> 19) & 0x3;
if (cVersion == 1) {
throw new UnsupportedAudioFileException(
"not a MPEG stream: wrong version");
}
int cSFIndex = (nHeader >> 10) & 0x3;
if (cSFIndex == 3) {
throw new UnsupportedAudioFileException(
"not a MPEG stream: wrong sampling rate");
}
AudioFormat format = new MpegAudioFormat(encoding, (float) nFrequency,
AudioSystem.NOT_SPECIFIED, nChannels, -1, FrameRate, true, af_properties);
return new MpegAudioFileFormat(MpegFileFormatType.MP3, format,
nTotalFrames, mLength, aff_properties);
}
}
}

View File

@ -0,0 +1,187 @@
/*
* Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This Code is based on the source provided with a tutorial by
* http://www.badlogicgames.com (as of 16.05.2013).
* The original source can be found here:
* http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/MP3Decoder.java
*/
package org.wyrez.audio.decoder;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import org.tritonus.sampled.file.jorbis.JorbisAudioFileReader;
import org.tritonus.share.sampled.FloatSampleBuffer;
/**
* A decoder for MP3 files with a maximum of 2 channels.
*
* @author Darth Affe
*/
public class VorbisDecoder implements Decoder {
private AudioInputStream in;
private FloatSampleBuffer buffer;
private byte[] bytes;
private int channels;
private float samplingRate;
/**
* Constructor, sets the input stream to read the MP3 file from. Audio files
* with more than 2 channels aren't supported!
*
* @param stream The input stream.
* @throws Exception in case the input stream couldn't be read properly
*/
public VorbisDecoder(InputStream stream) throws Exception {
InputStream in = new BufferedInputStream(stream, 1024 * 1024);
this.in = new JorbisAudioFileReader().getAudioInputStream(in);
AudioFormat baseFormat = this.in.getFormat();
this.channels = baseFormat.getChannels();
if (channels > 2) {
throw new IllegalArgumentException("more than 2 channels aren't supported");
}
this.samplingRate = baseFormat.getSampleRate();
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(), 16,
baseFormat.getChannels(),
baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), false);
this.in = AudioSystem.getAudioInputStream(format, this.in);
}
/**
* Tries to read in samples.length stereo samples. Returns the number of
* samples actually read. Guarantees that samples.length samples are read in
* if there was enough data in the stream.
*
* @param samples The samples array to write the samples to
* @return The number of samples actually read
*/
@Override
public int readSamplesStereo(float[] samples) {
if (buffer == null || buffer.getSampleCount() < samples.length / 2) {
buffer = new FloatSampleBuffer(in.getFormat().getChannels(), samples.length / 2, in.getFormat().getSampleRate());
bytes = new byte[buffer.getByteArrayBufferSize(in.getFormat())];
}
int read = 0;
int readBytes = 0;
try {
readBytes = in.read(bytes, read, bytes.length - read);
} catch (IOException e) {
return 0;
}
if (readBytes == -1) {
return 0;
}
read += readBytes;
while (readBytes != -1 && read != bytes.length) {
try {
readBytes = in.read(bytes, read, bytes.length - read);
} catch (IOException e) {
return 0;
}
read += readBytes;
}
int frameCount = bytes.length / in.getFormat().getFrameSize();
buffer.setSamplesFromBytes(bytes, 0, in.getFormat(), 0, frameCount);
for (int i = 0, j = 0; i < buffer.getSampleCount(); i++, j += 2) {
samples[j] = buffer.getChannel(0)[i];
samples[j + 1] = buffer.getChannel(buffer.getChannelCount() == 2 ? 1 : 0)[i];
}
return buffer.getSampleCount() * 2;
}
/**
* Tries to read in samples.length samples, merging stereo to a mono channel
* by averaging. Returns the number of samples actually read. Guarantees
* that samples.length samples are read in if there was enough data in the
* stream.
*
* @param samples The samples array to write the samples to
* @return The number of samples actually read
*/
@Override
public int readSamplesMergedMono(float[] samples) {
if (buffer == null || buffer.getSampleCount() < samples.length) {
buffer = new FloatSampleBuffer(in.getFormat().getChannels(), samples.length, in.getFormat().getSampleRate());
bytes = new byte[buffer.getByteArrayBufferSize(in.getFormat())];
}
int read = 0;
int readBytes = 0;
try {
readBytes = in.read(bytes, read, bytes.length - read);
} catch (IOException e) {
return 0;
}
if (readBytes == -1) {
return 0;
}
read += readBytes;
while (readBytes != -1 && read != bytes.length) {
try {
readBytes = in.read(bytes, read, bytes.length - read);
} catch (IOException e) {
return 0;
}
read += readBytes;
}
int frameCount = bytes.length / in.getFormat().getFrameSize();
buffer.setSamplesFromBytes(bytes, 0, in.getFormat(), 0, frameCount);
for (int i = 0; i < buffer.getSampleCount(); i++) {
if (buffer.getChannelCount() == 2) {
samples[i] = (buffer.getChannel(0)[i] + buffer.getChannel(1)[i]) / 2;
} else {
samples[i] = buffer.getChannel(0)[i];
}
}
return buffer.getSampleCount();
}
/**
* Returns the number of channels in the audio source. (This value can't be
* greater than 2)
*
* @returns The channel count
*/
@Override
public int getChannelCount() {
return channels;
}
/**
* Returns the sampling rate in Hz of the audio source.
*
* @returns the sampling rate as float
*/
@Override
public float getSamplingRate() {
return samplingRate;
}
}