|
@@ -40,6 +40,7 @@ import com.jme3.audio.AudioKey;
|
|
import com.jme3.util.BufferUtils;
|
|
import com.jme3.util.BufferUtils;
|
|
import de.jarnbjo.ogg.EndOfOggStreamException;
|
|
import de.jarnbjo.ogg.EndOfOggStreamException;
|
|
import de.jarnbjo.ogg.LogicalOggStream;
|
|
import de.jarnbjo.ogg.LogicalOggStream;
|
|
|
|
+import de.jarnbjo.ogg.PhysicalOggStream;
|
|
import de.jarnbjo.vorbis.IdentificationHeader;
|
|
import de.jarnbjo.vorbis.IdentificationHeader;
|
|
import de.jarnbjo.vorbis.VorbisStream;
|
|
import de.jarnbjo.vorbis.VorbisStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
@@ -52,13 +53,13 @@ public class OGGLoader implements AssetLoader {
|
|
|
|
|
|
// private static int BLOCK_SIZE = 4096*64;
|
|
// private static int BLOCK_SIZE = 4096*64;
|
|
|
|
|
|
- private UncachedOggStream oggStream;
|
|
|
|
|
|
+ private PhysicalOggStream oggStream;
|
|
private LogicalOggStream loStream;
|
|
private LogicalOggStream loStream;
|
|
private VorbisStream vorbisStream;
|
|
private VorbisStream vorbisStream;
|
|
|
|
|
|
// private CommentHeader commentHdr;
|
|
// private CommentHeader commentHdr;
|
|
private IdentificationHeader streamHdr;
|
|
private IdentificationHeader streamHdr;
|
|
-
|
|
|
|
|
|
+
|
|
private static class JOggInputStream extends InputStream {
|
|
private static class JOggInputStream extends InputStream {
|
|
|
|
|
|
private boolean endOfStream = false;
|
|
private boolean endOfStream = false;
|
|
@@ -105,6 +106,54 @@ public class OGGLoader implements AssetLoader {
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Returns the total of expected OGG bytes.
|
|
|
|
+ *
|
|
|
|
+ * @param dataBytesTotal The number of bytes in the input
|
|
|
|
+ * @return If the computed number of bytes is less than the number
|
|
|
|
+ * of bytes in the input, it is returned, otherwise the number
|
|
|
|
+ * of bytes in the input is returned.
|
|
|
|
+ */
|
|
|
|
+ private int getOggTotalBytes(int dataBytesTotal){
|
|
|
|
+ // Vorbis stream could have more samples than than the duration of the sound
|
|
|
|
+ // Must truncate.
|
|
|
|
+ int numSamples;
|
|
|
|
+ if (oggStream instanceof CachedOggStream){
|
|
|
|
+ CachedOggStream cachedOggStream = (CachedOggStream) oggStream;
|
|
|
|
+ numSamples = (int) cachedOggStream.getLastOggPage().getAbsoluteGranulePosition();
|
|
|
|
+ }else{
|
|
|
|
+ UncachedOggStream uncachedOggStream = (UncachedOggStream) oggStream;
|
|
|
|
+ numSamples = (int) uncachedOggStream.getLastOggPage().getAbsoluteGranulePosition();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Number of Samples * Number of Channels * Bytes Per Sample
|
|
|
|
+ int totalBytes = numSamples * streamHdr.getChannels() * 2;
|
|
|
|
+
|
|
|
|
+// System.out.println("Sample Rate: " + streamHdr.getSampleRate());
|
|
|
|
+// System.out.println("Channels: " + streamHdr.getChannels());
|
|
|
|
+// System.out.println("Stream Length: " + numSamples);
|
|
|
|
+// System.out.println("Bytes Calculated: " + totalBytes);
|
|
|
|
+// System.out.println("Bytes Available: " + dataBytes.length);
|
|
|
|
+
|
|
|
|
+ // Take the minimum of the number of bytes available
|
|
|
|
+ // and the expected duration of the audio.
|
|
|
|
+ return Math.min(totalBytes, dataBytesTotal);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private float computeStreamDuration(){
|
|
|
|
+ // for uncached stream sources, the granule position is not known.
|
|
|
|
+ if (oggStream instanceof UncachedOggStream)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ // 2 bytes(16bit) * channels * sampleRate
|
|
|
|
+ int bytesPerSec = 2 * streamHdr.getChannels() * streamHdr.getSampleRate();
|
|
|
|
+
|
|
|
|
+ // Don't know how many bytes are in input, pass MAX_VALUE
|
|
|
|
+ int totalBytes = getOggTotalBytes(Integer.MAX_VALUE);
|
|
|
|
+
|
|
|
|
+ return (float)totalBytes / bytesPerSec;
|
|
|
|
+ }
|
|
|
|
|
|
private ByteBuffer readToBuffer() throws IOException{
|
|
private ByteBuffer readToBuffer() throws IOException{
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
@@ -122,22 +171,8 @@ public class OGGLoader implements AssetLoader {
|
|
|
|
|
|
byte[] dataBytes = baos.toByteArray();
|
|
byte[] dataBytes = baos.toByteArray();
|
|
swapBytes(dataBytes, 0, dataBytes.length);
|
|
swapBytes(dataBytes, 0, dataBytes.length);
|
|
- // Vorbis stream could have more samples than than the duration of the sound
|
|
|
|
- // Must truncate.
|
|
|
|
- int numSamples = (int) oggStream.getLastOggPage().getAbsoluteGranulePosition();
|
|
|
|
-
|
|
|
|
- // Number of Samples * Number of Channels * Bytes Per Sample
|
|
|
|
- int totalBytes = numSamples * streamHdr.getChannels() * 2;
|
|
|
|
-
|
|
|
|
-// System.out.println("Sample Rate: " + streamHdr.getSampleRate());
|
|
|
|
-// System.out.println("Channels: " + streamHdr.getChannels());
|
|
|
|
-// System.out.println("Stream Length: " + numSamples);
|
|
|
|
-// System.out.println("Bytes Calculated: " + totalBytes);
|
|
|
|
-// System.out.println("Bytes Available: " + dataBytes.length);
|
|
|
|
-
|
|
|
|
- // Take the minimum of the number of bytes available
|
|
|
|
- // and the expected duration of the audio.
|
|
|
|
- int bytesToCopy = Math.min(totalBytes, dataBytes.length);
|
|
|
|
|
|
+
|
|
|
|
+ int bytesToCopy = getOggTotalBytes( dataBytes.length );
|
|
|
|
|
|
ByteBuffer data = BufferUtils.createByteBuffer(bytesToCopy);
|
|
ByteBuffer data = BufferUtils.createByteBuffer(bytesToCopy);
|
|
data.put(dataBytes, 0, bytesToCopy).flip();
|
|
data.put(dataBytes, 0, bytesToCopy).flip();
|
|
@@ -149,7 +184,7 @@ public class OGGLoader implements AssetLoader {
|
|
return data;
|
|
return data;
|
|
}
|
|
}
|
|
|
|
|
|
- private static final void swapBytes(byte[] b, int off, int len) {
|
|
|
|
|
|
+ private static void swapBytes(byte[] b, int off, int len) {
|
|
byte tempByte;
|
|
byte tempByte;
|
|
for (int i = off; i < (off+len); i+=2) {
|
|
for (int i = off; i < (off+len); i+=2) {
|
|
tempByte = b[i];
|
|
tempByte = b[i];
|
|
@@ -163,8 +198,20 @@ public class OGGLoader implements AssetLoader {
|
|
}
|
|
}
|
|
|
|
|
|
public Object load(AssetInfo info) throws IOException {
|
|
public Object load(AssetInfo info) throws IOException {
|
|
|
|
+ if (!(info.getKey() instanceof AudioKey)){
|
|
|
|
+ throw new IllegalArgumentException("Audio assets must be loaded using an AudioKey");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ AudioKey key = (AudioKey) info.getKey();
|
|
|
|
+ boolean readStream = key.isStream();
|
|
|
|
+ boolean streamCache = key.useStreamCache();
|
|
|
|
+
|
|
InputStream in = info.openStream();
|
|
InputStream in = info.openStream();
|
|
- oggStream = new UncachedOggStream(in);
|
|
|
|
|
|
+ if (readStream && streamCache){
|
|
|
|
+ oggStream = new CachedOggStream(in);
|
|
|
|
+ }else{
|
|
|
|
+ oggStream = new UncachedOggStream(in);
|
|
|
|
+ }
|
|
|
|
|
|
Collection<LogicalOggStream> streams = oggStream.getLogicalStreams();
|
|
Collection<LogicalOggStream> streams = oggStream.getLogicalStreams();
|
|
loStream = streams.iterator().next();
|
|
loStream = streams.iterator().next();
|
|
@@ -176,9 +223,7 @@ public class OGGLoader implements AssetLoader {
|
|
vorbisStream = new VorbisStream(loStream);
|
|
vorbisStream = new VorbisStream(loStream);
|
|
streamHdr = vorbisStream.getIdentificationHeader();
|
|
streamHdr = vorbisStream.getIdentificationHeader();
|
|
// commentHdr = vorbisStream.getCommentHeader();
|
|
// commentHdr = vorbisStream.getCommentHeader();
|
|
-
|
|
|
|
- boolean readStream = ((AudioKey)info.getKey()).isStream();
|
|
|
|
-
|
|
|
|
|
|
+
|
|
if (!readStream){
|
|
if (!readStream){
|
|
AudioBuffer audioBuffer = new AudioBuffer();
|
|
AudioBuffer audioBuffer = new AudioBuffer();
|
|
audioBuffer.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
|
|
audioBuffer.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
|
|
@@ -187,7 +232,11 @@ public class OGGLoader implements AssetLoader {
|
|
}else{
|
|
}else{
|
|
AudioStream audioStream = new AudioStream();
|
|
AudioStream audioStream = new AudioStream();
|
|
audioStream.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
|
|
audioStream.setupFormat(streamHdr.getChannels(), 16, streamHdr.getSampleRate());
|
|
- audioStream.updateData(readToStream(), -1);
|
|
|
|
|
|
+
|
|
|
|
+ // might return -1 if unknown
|
|
|
|
+ float streamDuration = computeStreamDuration();
|
|
|
|
+
|
|
|
|
+ audioStream.updateData(readToStream(), streamDuration);
|
|
return audioStream;
|
|
return audioStream;
|
|
}
|
|
}
|
|
}
|
|
}
|