From bc3776633ccad81199a96ff8116195133d862395 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Wed, 14 Aug 2013 06:43:42 +0200 Subject: GLMediaPlayer Multithreaded Decoding: AudioSink (Part-2) - WIP - AudioSink.AudioDataFormat - add fixedP (fixed-point or floating-point) - AudioSink - rename 'buffer count' to 'frame count' - add setPlaySpeed(..) - add isPlaying() - add play() - add pause() - add flush() - add: getFrameCount(), getQueuedFrameCount(), getFreeFrameCount(), getEnqueuedFrameCount(), - rename: writeData() -> enqueueData(..) - ALAudioSink - multithreaded usage - make ALCcontext current per thread, now required for multithreaded use Use RecursiveLock encapsulating the ALCcontext's makeCurrent/release/destroy, since the native operations seem to be buggy. NOTE: Think about adding these general methods to ALCcontext - implement new methods - --- .../com/jogamp/opengl/util/av/AudioSink.java | 117 ++++++++++++++++----- .../jogamp/opengl/util/av/AudioSinkFactory.java | 5 +- 2 files changed, 95 insertions(+), 27 deletions(-) (limited to 'src/jogl/classes/com/jogamp/opengl/util/av') diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java index 5caeb969a..ba785ac31 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java @@ -41,12 +41,13 @@ public interface AudioSink { * Specifies the audio data format. */ public static class AudioDataFormat { - public AudioDataFormat(AudioDataType dataType, int sampleRate, int sampleSize, int channelCount, boolean signed, boolean littleEndian) { + public AudioDataFormat(AudioDataType dataType, int sampleRate, int sampleSize, int channelCount, boolean signed, boolean fixedP, boolean littleEndian) { this.dataType = dataType; this.sampleRate = sampleRate; this.sampleSize = sampleSize; this.channelCount = channelCount; this.signed = signed; + this.fixedP = fixedP; this.littleEndian = littleEndian; } /** Audio data type. */ @@ -58,30 +59,32 @@ public interface AudioSink { /** Number of channels. */ public final int channelCount; public final boolean signed; + /** Fixed or floating point values. Floating point 'float' has {@link #sampleSize} 32, 'double' has {@link #sampleSize} 64, */ + public final boolean fixedP; public final boolean littleEndian; public String toString() { return "AudioDataFormat[type "+dataType+", sampleRate "+sampleRate+", sampleSize "+sampleSize+", channelCount "+channelCount+ - ", signed "+signed+", "+(littleEndian?"little":"big")+"endian]"; } + ", signed "+signed+", fixedP "+fixedP+", "+(littleEndian?"little":"big")+"endian]"; } } - /** Default {@link AudioDataFormat}, [type PCM, sampleRate 44100, sampleSize 16, channelCount 2, signed, littleEndian]. */ - public static final AudioDataFormat DefaultFormat = new AudioDataFormat(AudioDataType.PCM, 44100, 16, 2, true /* signed */, true /* littleEndian */); + /** Default {@link AudioDataFormat}, [type PCM, sampleRate 44100, sampleSize 16, channelCount 2, signed, fixedP, littleEndian]. */ + public static final AudioDataFormat DefaultFormat = new AudioDataFormat(AudioDataType.PCM, 44100, 16, 2, true /* signed */, true /* fixed point */, true /* littleEndian */); public static class AudioFrame { public final ByteBuffer data; public final int dataSize; - public final int audioPTS; + public final int pts; - public AudioFrame(ByteBuffer data, int dataSize, int audioPTS) { + public AudioFrame(ByteBuffer data, int dataSize, int pts) { if( dataSize > data.remaining() ) { throw new IllegalArgumentException("Give size "+dataSize+" exceeds remaining bytes in ls "+data+". "+this); } this.data=data; this.dataSize=dataSize; - this.audioPTS=audioPTS; + this.pts=pts; } - public String toString() { return "AudioFrame[apts "+audioPTS+", data "+data+", payloadSize "+dataSize+"]"; } + public String toString() { return "AudioFrame[apts "+pts+", data "+data+", payloadSize "+dataSize+"]"; } } /** @@ -94,6 +97,19 @@ public interface AudioSink { */ public boolean isInitialized(); + /** Returns the playback speed. */ + public float getPlaySpeed(); + + /** + * Sets the playback speed. + *

+ * Play speed is set to normal, i.e. 1.0f + * if abs(1.0f - rate) < 0.01f to simplify test. + *

+ * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. + */ + public boolean setPlaySpeed(float s); + /** * Returns the preferred {@link AudioDataFormat} by this sink. *

@@ -117,52 +133,101 @@ public interface AudioSink { * The {@link #DefaultFormat} should be supported by all implementations. *

* @param requestedFormat the requested {@link AudioDataFormat}. - * @param bufferCount number of buffers for sink + * @param frameCount number of frames to queue in this sink * @return if successful the chosen AudioDataFormat based on the requestedFormat and this sinks capabilities, otherwise null. */ - public AudioDataFormat initSink(AudioDataFormat requestedFormat, int bufferCount); - + public AudioDataFormat initSink(AudioDataFormat requestedFormat, int frameCount); + + /** + * Returns true, if {@link #play()} has been requested and the sink is still playing, + * otherwise false. + */ + public boolean isPlaying(); + + /** + * Play buffers queued via {@link #enqueueData(AudioFrame)} from current internal position. + * If no buffers are yet queued or the queue runs empty, playback is being continued when buffers are enqueued later on. + * @see #enqueueData(AudioFrame) + * @see #pause() + */ + public void play(); + + /** + * Pause playing buffers while keeping enqueued data incl. it's internal position. + * @see #play() + * @see #flush() + * @see #enqueueData(AudioFrame) + */ + public void pause(); + + /** + * Flush all queued buffers, implies {@link #pause()}. + *

+ * {@link #initSink(AudioDataFormat, int)} must be called first. + *

+ * @see #play() + * @see #pause() + * @see #enqueueData(AudioFrame) + */ + public void flush(); /** Destroys this instance, i.e. closes all streams and devices allocated. */ public void destroy(); /** - * Returns the number of bytes queued for playing. + * Returns the number of allocated buffers as requested by + * {@link #initSink(AudioDataFormat, int)}. + */ + public int getFrameCount(); + + /** @return the current enqueued frames count since {@link #initSink(AudioDataFormat, int)}. */ + public int getEnqueuedFrameCount(); + + /** + * Returns the current number of frames queued for playing. + *

+ * {@link #initSink(AudioDataFormat, int)} must be called first. + *

+ */ + public int getQueuedFrameCount(); + + /** + * Returns the current number of bytes queued for playing. *

- * {@link #initSink(AudioDataFormat)} must be called first. + * {@link #initSink(AudioDataFormat, int)} must be called first. *

*/ public int getQueuedByteCount(); /** - * Returns the queued buffer time in milliseconds for playing. + * Returns the current queued frame time in milliseconds for playing. *

- * {@link #initSink(AudioDataFormat)} must be called first. + * {@link #initSink(AudioDataFormat, int)} must be called first. *

*/ public int getQueuedTime(); /** - * Returns the number of buffers in the sink available for writing. - *

- * {@link #initSink(AudioDataFormat)} must be called first. - *

+ * Return the current audio presentation timestamp (PTS) in milliseconds. */ - public int getWritableBufferCount(); + public int getPTS(); /** - * Returns true if data is available to be written in the sink. + * Returns the current number of frames in the sink available for writing. *

- * {@link #initSink(AudioDataFormat)} must be called first. + * {@link #initSink(AudioDataFormat, int)} must be called first. *

*/ - public boolean isDataAvailable(int data_size); - + public int getFreeFrameCount(); + /** - * Writes the remaining bytes of the given direct ByteBuffer to this sink. + * Enqueue the remaining bytes of the given {@link AudioFrame}'s direct ByteBuffer to this sink. *

* The data must comply with the chosen {@link AudioDataFormat} as returned by {@link #initSink(AudioDataFormat)}. *

+ *

+ * {@link #initSink(AudioDataFormat, int)} must be called first. + *

*/ - public void writeData(AudioFrame audioFrame); + public void enqueueData(AudioFrame audioFrame); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java index 40321fb6f..a6a14f7dd 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java @@ -42,10 +42,13 @@ public class AudioSinkFactory { sink = create(cl, JavaAudioSinkClazzName); } if( null == sink ) { - sink = new NullAudioSink(); + sink = createNull(); } return sink; } + public static AudioSink createNull() { + return new NullAudioSink(); + } public static AudioSink create(final ClassLoader cl, String implName) { final AudioSink audioSink; -- cgit v1.2.3