added Vorbis decoder, replaced own mp3 file reader with the default one
This commit is contained in:
parent
6c393ef77a
commit
b44363990d
BIN
AudioProcessing/lib/jogg-0.0.7.jar
Normal file
BIN
AudioProcessing/lib/jogg-0.0.7.jar
Normal file
Binary file not shown.
BIN
AudioProcessing/lib/jorbis-0.0.15.jar
Normal file
BIN
AudioProcessing/lib/jorbis-0.0.15.jar
Normal file
Binary file not shown.
BIN
AudioProcessing/lib/tritonus_jorbis-0.3.6.jar
Normal file
BIN
AudioProcessing/lib/tritonus_jorbis-0.3.6.jar
Normal file
Binary file not shown.
@ -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>
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user