|
@@ -1,278 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright (c) 2009-2010 jMonkeyEngine
|
|
|
- * All rights reserved.
|
|
|
- *
|
|
|
- * Redistribution and use in source and binary forms, with or without
|
|
|
- * modification, are permitted provided that the following conditions are
|
|
|
- * met:
|
|
|
- *
|
|
|
- * * Redistributions of source code must retain the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer.
|
|
|
- *
|
|
|
- * * Redistributions in binary form must reproduce the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer in the
|
|
|
- * documentation and/or other materials provided with the distribution.
|
|
|
- *
|
|
|
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
|
- * may be used to endorse or promote products derived from this software
|
|
|
- * without specific prior written permission.
|
|
|
- *
|
|
|
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
- */
|
|
|
-
|
|
|
-package com.jme3.video.plugins.jheora;
|
|
|
-
|
|
|
-import com.jcraft.jogg.Packet;
|
|
|
-import com.jcraft.jorbis.Block;
|
|
|
-import com.jcraft.jorbis.Comment;
|
|
|
-import com.jcraft.jorbis.DspState;
|
|
|
-import com.jcraft.jorbis.Info;
|
|
|
-import com.jme3.audio.AudioStream;
|
|
|
-import com.jme3.video.Clock;
|
|
|
-import com.jme3.video.RingBuffer;
|
|
|
-import com.jme3.video.SystemClock;
|
|
|
-import java.io.InputStream;
|
|
|
-
|
|
|
-@Deprecated
|
|
|
-public class ADecoder extends InputStream implements Clock {
|
|
|
-
|
|
|
- private int packetIndex = 0;
|
|
|
-
|
|
|
- private final DspState dsp;
|
|
|
- private Block block;
|
|
|
-
|
|
|
- private Info info;
|
|
|
- private Comment comment;
|
|
|
-
|
|
|
- private float[][][] pcmAll = new float[1][][];
|
|
|
- private int[] index;
|
|
|
-
|
|
|
- private AudioStream stream;
|
|
|
- private RingBuffer ringBuffer = new RingBuffer(48000 * 2 * 2 * 2);
|
|
|
-
|
|
|
- private int UNCOMP_BUFSIZE = 4096;
|
|
|
- private byte[] uncompBuf = new byte[UNCOMP_BUFSIZE];
|
|
|
-
|
|
|
- private long lastPts = 0;
|
|
|
- private long lastWritten = 0;
|
|
|
- private long lastRead = 0;
|
|
|
- private long lastPtsRead = 0;
|
|
|
- private long lastPtsWrite = 0;
|
|
|
-
|
|
|
- private long timeDiffSum = 0;
|
|
|
- private int timesDesynced = 0;
|
|
|
- private Clock masterClock;
|
|
|
-
|
|
|
- public ADecoder(){
|
|
|
- info = new Info();
|
|
|
- info.init();
|
|
|
-
|
|
|
- comment = new Comment();
|
|
|
- comment.init();
|
|
|
-
|
|
|
- dsp = new DspState();
|
|
|
- block = new Block(dsp);
|
|
|
- }
|
|
|
-
|
|
|
- public void setMasterClock(Clock masterClock) {
|
|
|
- this.masterClock = masterClock;
|
|
|
- }
|
|
|
-
|
|
|
- public AudioStream getAudioStream(){
|
|
|
- return stream;
|
|
|
- }
|
|
|
-
|
|
|
- public long getTime(){
|
|
|
- long bytesRead = ringBuffer.getTotalRead();
|
|
|
- if (bytesRead == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- //long diff = bytesRead - lastWritten;
|
|
|
- long diff = bytesRead;
|
|
|
- long diffNs = (diff * Clock.SECONDS_TO_NANOS) / (2 * info.channels * info.rate);
|
|
|
- long timeSinceLastRead = System.nanoTime() - lastPtsRead;
|
|
|
- return /*lastPts +*/ diffNs + timeSinceLastRead;
|
|
|
- }
|
|
|
-
|
|
|
- public double getTimeSeconds(){
|
|
|
- return (double) getTime() / Clock.SECONDS_TO_NANOS;
|
|
|
- }
|
|
|
-
|
|
|
- public int read(){
|
|
|
- byte[] buf = new byte[1];
|
|
|
- int read = read(buf, 0, 1);
|
|
|
- if (read < 0)
|
|
|
- return -1;
|
|
|
- else
|
|
|
- return buf[0] & 0xff;
|
|
|
- }
|
|
|
-
|
|
|
- private static final long NOSYNC_THRESH = 3 * Clock.SECONDS_TO_NANOS,
|
|
|
- AUDIO_DIFF_THRESH = Clock.SECONDS_TO_NANOS / 2;
|
|
|
-
|
|
|
- private static final int NUM_DIFFS_FOR_SYNC = 5;
|
|
|
- private static final double PRODUCT_FOR_PREV = 6.0 / 10.0,
|
|
|
- PRODUCT_FOR_PREV_INV = 4.0 / 10.0;
|
|
|
-
|
|
|
- private int needToSkip = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * Only useful if audio is synced to something else.
|
|
|
- */
|
|
|
- private void sync(){
|
|
|
- if (needToSkip > 0){
|
|
|
- int skipped = ringBuffer.skip(needToSkip);
|
|
|
- System.out.println("Skipped: "+skipped);
|
|
|
- needToSkip -= skipped;
|
|
|
- }
|
|
|
-
|
|
|
- long masterTime = masterClock.getTime();
|
|
|
- long audioTime = getTime();
|
|
|
- long diff = audioTime - masterTime;
|
|
|
- if (diff < NOSYNC_THRESH){
|
|
|
- timeDiffSum = diff + (long) (timeDiffSum * PRODUCT_FOR_PREV);
|
|
|
- if (timesDesynced < NUM_DIFFS_FOR_SYNC){
|
|
|
- timesDesynced ++;
|
|
|
- }else{
|
|
|
- long avgDiff = (long) (timeDiffSum * PRODUCT_FOR_PREV_INV);
|
|
|
- if (Math.abs(avgDiff) >= AUDIO_DIFF_THRESH){
|
|
|
- if (diff < 0){
|
|
|
- int toSkip = (int) ((-diff * 2 * info.channels * info.rate) / Clock.SECONDS_TO_NANOS);
|
|
|
- int skipped = ringBuffer.skip(toSkip);
|
|
|
- System.out.println("Skipped: "+skipped);
|
|
|
- if (skipped < toSkip)
|
|
|
- needToSkip = toSkip - skipped;
|
|
|
-
|
|
|
- timeDiffSum = 0;
|
|
|
- timesDesynced = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }else{
|
|
|
- timesDesynced = 0;
|
|
|
- timeDiffSum = 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int read(byte[] buf, int offset, int length){
|
|
|
-// int diff = (int) (ringBuffer.getTotalWritten() - ringBuffer.getTotalRead());
|
|
|
-// if ( diff > info.rate * info.channels * 2){
|
|
|
-// System.out.println("Warning: more than 1 second lag for audio. Adjusting..");
|
|
|
-// ringBuffer.skip( diff );
|
|
|
-// }
|
|
|
-
|
|
|
-
|
|
|
-// if (masterClock != null)
|
|
|
-// sync();
|
|
|
-
|
|
|
- int r = ringBuffer.read(buf, offset, length);
|
|
|
- if (r <= 0){
|
|
|
-// // put silence
|
|
|
- for (int i = 0; i < length; i++){
|
|
|
- buf[offset + i] = 0x0;
|
|
|
- }
|
|
|
- return length;
|
|
|
- }else{
|
|
|
- lastPtsRead = System.nanoTime();
|
|
|
- }
|
|
|
- return r;
|
|
|
- }
|
|
|
-
|
|
|
- public void decodeDsp(Packet packet){
|
|
|
- if (block.synthesis(packet) == 0) {
|
|
|
- dsp.synthesis_blockin(block);
|
|
|
- }
|
|
|
-
|
|
|
- int samplesAvail;
|
|
|
- int channels = info.channels;
|
|
|
- while ((samplesAvail = dsp.synthesis_pcmout(pcmAll, index)) > 0) {
|
|
|
- float[][] pcm = pcmAll[0];
|
|
|
- int samplesCanRead = UNCOMP_BUFSIZE / (channels*2);
|
|
|
- int samplesToRead = (samplesAvail < samplesCanRead ? samplesAvail : samplesCanRead);
|
|
|
-
|
|
|
- // convert floats to 16 bit signed ints and interleave
|
|
|
- for (int i = 0; i < channels; i++) {
|
|
|
- // each sample is two bytes, the sample for the 2nd
|
|
|
- // channel is at index 2, etc.
|
|
|
- int writeOff = i * 2;
|
|
|
- int readOff = index[i];
|
|
|
- for (int j = 0; j < samplesToRead; j++) {
|
|
|
- int val = (int) (pcm[i][readOff + j] * 32767.0);
|
|
|
- // guard against clipping
|
|
|
- if (val > 32767) {
|
|
|
- val = 32767;
|
|
|
- }if (val < -32768) {
|
|
|
- val = -32768;
|
|
|
- }
|
|
|
- uncompBuf[writeOff] = (byte) (val);
|
|
|
- uncompBuf[writeOff + 1] = (byte) (val >> 8);
|
|
|
-
|
|
|
- writeOff += 2 * channels; // each sample is 2 bytes
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ringBuffer.write(uncompBuf, 0, samplesToRead * channels * 2);
|
|
|
-
|
|
|
- // tell vorbis how many samples were actualy consumed
|
|
|
- dsp.synthesis_read(samplesToRead);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void close(){
|
|
|
- }
|
|
|
-
|
|
|
- public void decode(Packet packet){
|
|
|
- if (packetIndex < 3) {
|
|
|
- if (info.synthesis_headerin(comment, packet) < 0) {
|
|
|
- // error case; not a Vorbis header
|
|
|
- System.err.println("does not contain Vorbis audio data.");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (packetIndex == 2) {
|
|
|
- dsp.synthesis_init(info);
|
|
|
- block.init(dsp);
|
|
|
- System.out.println("vorbis: "+info);
|
|
|
- System.out.println(comment.toString());
|
|
|
- index = new int[info.channels];
|
|
|
-
|
|
|
- if (stream == null){
|
|
|
- stream = new AudioStream();
|
|
|
- stream.setupFormat(info.channels, 16, info.rate);
|
|
|
- stream.updateData(this, -1);
|
|
|
- }
|
|
|
-
|
|
|
- if (masterClock instanceof SystemClock){
|
|
|
- SystemClock clock = (SystemClock) masterClock;
|
|
|
- if (clock.needReset()){
|
|
|
- clock.reset();
|
|
|
- System.out.println("Note: master clock was reset by audio");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- long gp = packet.granulepos;
|
|
|
- if (gp != -1){
|
|
|
- lastPts = (gp * Clock.SECONDS_TO_NANOS) / info.rate;
|
|
|
- lastWritten = ringBuffer.getTotalWritten();
|
|
|
- lastRead = ringBuffer.getTotalRead();
|
|
|
- lastPtsWrite = System.nanoTime();
|
|
|
- }
|
|
|
-
|
|
|
- decodeDsp(packet);
|
|
|
- }
|
|
|
- packetIndex++;
|
|
|
- }
|
|
|
-
|
|
|
-}
|