Sfoglia il codice sorgente

AudioSource: add method to get playback time
As was requested on the forum, getting playback time / position is needed to perform proper audio / video synchronization.

Kirill Vainer 10 anni fa
parent
commit
7393f79165

+ 5 - 0
jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java

@@ -525,4 +525,9 @@ public class AndroidMediaPlayerAudioRenderer implements AudioRenderer,
     @Override
     public void deleteFilter(Filter filter) {
     }
+
+    @Override
+    public float getSourcePlaybackTime(AudioSource src) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }

+ 8 - 0
jme3-core/src/main/java/com/jme3/audio/AudioNode.java

@@ -409,6 +409,14 @@ public class AudioNode extends Node implements AudioSource {
             play();
         }
     }
+    
+    @Override
+    public float getPlaybackTime() {
+        if (channel >= 0)
+            return getRenderer().getSourcePlaybackTime(this);
+        else
+            return 0;
+    }
 
     public Vector3f getPosition() {
         return getWorldTranslation();

+ 1 - 0
jme3-core/src/main/java/com/jme3/audio/AudioRenderer.java

@@ -59,6 +59,7 @@ public interface AudioRenderer {
 
     public void updateSourceParam(AudioSource src, AudioParam param);
     public void updateListenerParam(Listener listener, ListenerParam param);
+    public float getSourcePlaybackTime(AudioSource src);
 
     public void deleteFilter(Filter filter);
     public void deleteAudioData(AudioData ad);

+ 5 - 0
jme3-core/src/main/java/com/jme3/audio/AudioSource.java

@@ -95,6 +95,11 @@ public interface AudioSource {
      * @return the time offset in the sound sample when to start playing.
      */
     public float getTimeOffset();
+    
+    /**
+     * @return the current playback position of the source in seconds.
+     */
+    public float getPlaybackTime();
 
     /**
      * @return The velocity of the audio source.

+ 13 - 0
jme3-core/src/main/java/com/jme3/audio/AudioStream.java

@@ -54,6 +54,8 @@ public class AudioStream extends AudioData implements Closeable {
     protected boolean eof = false;
     protected int[] ids;
 
+    protected int unqueuedBuffersBytes = 0;
+    
     public AudioStream() {
         super();
     }
@@ -196,10 +198,21 @@ public class AudioStream extends AudioData implements Closeable {
         return in instanceof SeekableStream;
     }
 
+    public int getUnqueuedBufferBytes() {
+        return unqueuedBuffersBytes;
+    }
+
+    public void setUnqueuedBufferBytes(int unqueuedBuffers) {
+        this.unqueuedBuffersBytes = unqueuedBuffers;
+    }
+    
     public void setTime(float time) {
         if (in instanceof SeekableStream) {
             ((SeekableStream) in).setTime(time);
             eof = false;
+            
+            // TODO: when we actually support seeking, this will need to be properly set.
+            unqueuedBuffersBytes = 0; 
         } else {
             throw new IllegalStateException(
                     "Cannot use setTime on a stream that "

+ 60 - 0
jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java

@@ -301,6 +301,58 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
         f.clearUpdateNeeded();
     }
 
+    @Override
+    public float getSourcePlaybackTime(AudioSource src) {
+        checkDead();
+        synchronized (threadLock) {
+            if (audioDisabled) {
+                return 0;
+            }
+            
+            // See comment in updateSourceParam().
+            if (src.getChannel() < 0) {
+                return 0;
+            }
+            
+            int id = channels[src.getChannel()];
+            AudioData data = src.getAudioData();
+            int playbackOffsetBytes = 0;
+            
+            if (data instanceof AudioStream) {
+                // Because audio streams are processed in buffer chunks, 
+                // we have to compute the amount of time the stream was already
+                // been playing based on the number of buffers that were processed.
+                AudioStream stream = (AudioStream) data;
+                
+                // NOTE: the assumption is that all enqueued buffers are the same size.
+                //       this is currently enforced by fillBuffer().
+                
+                // The number of unenqueued bytes that the decoder thread
+                // keeps track of.
+                int unqueuedBytes = stream.getUnqueuedBufferBytes();
+                
+                // Additional processed buffers that the decoder thread
+                // did not unenqueue yet (it only updates 20 times per second).
+                int unqueuedBytesExtra = al.alGetSourcei(id, AL_BUFFERS_PROCESSED) * BUFFER_SIZE;
+                
+                // Total additional bytes that need to be considered.
+                playbackOffsetBytes = unqueuedBytes; // + unqueuedBytesExtra;
+            }
+            
+            // Add byte offset from source (for both streams and buffers)
+            playbackOffsetBytes += al.alGetSourcei(id, AL_BYTE_OFFSET);
+            
+            // Compute time value from bytes
+            // E.g. for 44100 source with 2 channels and 16 bits per sample:
+            //    (44100 * 2 * 16 / 8) = 176400
+            int bytesPerSecond = (data.getSampleRate() * 
+                                  data.getChannels() * 
+                                  data.getBitsPerSample() / 8);
+            
+            return (float)playbackOffsetBytes / bytesPerSecond;
+        }
+    }
+    
     public void updateSourceParam(AudioSource src, AudioParam param) {
         checkDead();
         synchronized (threadLock) {
@@ -648,6 +700,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
     private boolean fillStreamingSource(int sourceId, AudioStream stream, boolean looping) {
         boolean success = false;
         int processed = al.alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
+        int unqueuedBufferBytes = 0;
         
         for (int i = 0; i < processed; i++) {
             int buffer;
@@ -656,6 +709,11 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
             al.alSourceUnqueueBuffers(sourceId, 1, ib);
             buffer = ib.get(0);
             
+            // XXX: assume that reading from AudioStream always 
+            // gives BUFFER_SIZE amount of bytes! This might not always
+            // be the case...
+            unqueuedBufferBytes += BUFFER_SIZE;
+            
             boolean active = fillBuffer(stream, buffer);
             
             if (!active && !stream.isEOF()) {
@@ -682,6 +740,8 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
                 break;
             }
         }
+        
+        stream.setUnqueuedBufferBytes(stream.getUnqueuedBufferBytes() + unqueuedBufferBytes);
 
         return success;
     }