From d42b236ef139dcc8d8713535893c7870b55d420c Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Wed, 11 Dec 2013 20:50:43 +0100 Subject: ALAudioSink: Fix stop and flush of OpenAL source - stopImpl() shall always issue alSourceStop(..) if state is not STOPPED - Remove 'flush' hint for dequeueBuffer(..), we perform proper flush in respective method, see below - flush() needs to issue: - stopImpl() - which should already dequeue all buffers - Explicitly dequeue all buffers: via 'alSourcei(alSource[0], AL.AL_BUFFER, 0)' - Then dequeue manually processed buffers: dequeueBuffer( false /* wait */ ); - And dequeue _all_ buffers: dequeueForceAll(); --- .../jogamp/opengl/openal/av/ALAudioSink.java | 240 +++++++++++---------- 1 file changed, 125 insertions(+), 115 deletions(-) (limited to 'src/jogl/classes/jogamp/opengl') diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java index ecb6b60e8..1229eb7b8 100644 --- a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java +++ b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. 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. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community OR @@ -20,7 +20,7 @@ * 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. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -53,22 +53,22 @@ public class ALAudioSink implements AudioSink { private static final ALC alc; private static final AL al; private static final ALExt alExt; - private static final boolean staticAvailable; - + private static final boolean staticAvailable; + private String deviceSpecifier; private ALCdevice device; private boolean hasSOFTBufferSamples; - private AudioFormat preferredAudioFormat; + private AudioFormat preferredAudioFormat; private ALCcontext context; private final RecursiveLock lock = LockFactory.createRecursiveLock(); /** Playback speed, range [0.5 - 2.0], default 1.0. */ private float playSpeed; private float volume = 1.0f; - + static class ALAudioFrame extends AudioFrame { private final int alBuffer; - + ALAudioFrame(int alBuffer) { this.alBuffer = alBuffer; } @@ -76,20 +76,20 @@ public class ALAudioSink implements AudioSink { super(pts, duration, dataSize); this.alBuffer = alBuffer; } - + /** Get this frame's OpenAL buffer name */ public final int getALBuffer() { return alBuffer; } - - public String toString() { + + public String toString() { return "ALAudioFrame[pts " + pts + " ms, l " + duration + " ms, " + byteSize + " bytes, buffer "+alBuffer+"]"; } } - + // private ALAudioFrame[] alFrames = null; private int[] alBufferNames = null; private int frameGrowAmount = 0; private int frameLimit = 0; - + private Ringbuffer alFramesAvail = null; private Ringbuffer alFramesPlaying = null; private volatile int alBufferBytesQueued = 0; @@ -102,7 +102,7 @@ public class ALAudioSink implements AudioSink { private int alSampleType; private int alFormat; private boolean initialized; - + private volatile boolean playRequested = false; static { @@ -110,7 +110,7 @@ public class ALAudioSink implements AudioSink { AL _al = null; ALExt _alExt = null; try { - _alc = ALFactory.getALC(); + _alc = ALFactory.getALC(); _al = ALFactory.getAL(); _alExt = ALFactory.getALExt(); } catch(Throwable t) { @@ -124,41 +124,41 @@ public class ALAudioSink implements AudioSink { alExt = _alExt; staticAvailable = null != alc && null != al && null != alExt; } - + public ALAudioSink() { initialized = false; chosenFormat = null; - + if( !staticAvailable ) { return; } - + try { // Get handle to default device. device = alc.alcOpenDevice(null); if (device == null) { throw new RuntimeException("ALAudioSink: Error opening default OpenAL device"); } - + // Get the device specifier. deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER); if (deviceSpecifier == null) { throw new RuntimeException("ALAudioSink: Error getting specifier for default OpenAL device"); } - + // Create audio context. context = alc.alcCreateContext(device, null); if (context == null) { throw new RuntimeException("ALAudioSink: Error creating OpenAL context for "+deviceSpecifier); } - + lockContext(); try { // Check for an error. if ( alc.alcGetError(device) != ALC.ALC_NO_ERROR ) { throw new RuntimeException("ALAudioSink: Error making OpenAL context current"); } - + hasSOFTBufferSamples = al.alIsExtensionPresent(AL_SOFT_buffer_samples); preferredAudioFormat = queryPreferredAudioFormat(); if( DEBUG ) { @@ -168,7 +168,7 @@ public class ALAudioSink implements AudioSink { System.out.println("ALAudioSink: hasSOFTBufferSamples "+hasSOFTBufferSamples); System.out.println("ALAudioSink: preferredAudioFormat "+preferredAudioFormat); } - + // Create source { alSource = new int[1]; @@ -177,10 +177,10 @@ public class ALAudioSink implements AudioSink { if( err != AL.AL_NO_ERROR ) { alSource = null; throw new RuntimeException("ALAudioSink: Error generating Source: 0x"+Integer.toHexString(err)); - } + } } - - if( DEBUG ) { + + if( DEBUG ) { System.err.println("ALAudioSink: Using device: " + deviceSpecifier); } initialized = true; @@ -195,7 +195,7 @@ public class ALAudioSink implements AudioSink { destroy(); } } - + private final AudioFormat queryPreferredAudioFormat() { int sampleRate = DefaultFormat.sampleRate; final int[] value = new int[1]; @@ -205,7 +205,7 @@ public class ALAudioSink implements AudioSink { } return new AudioFormat(sampleRate, DefaultFormat.sampleSize, DefaultFormat.channelCount, DefaultFormat.signed, DefaultFormat.fixedP, DefaultFormat.planar, DefaultFormat.littleEndian); } - + private final void lockContext() { lock.lock(); alc.alcMakeContextCurrent(context); @@ -236,12 +236,12 @@ public class ALAudioSink implements AudioSink { lock.unlock(); } } - + @Override public final String toString() { final int alSrcName = null != alSource ? alSource[0] : 0; final int alBuffersLen = null != alBufferNames ? alBufferNames.length : 0; - final int ctxHash = context != null ? context.hashCode() : 0; + final int ctxHash = context != null ? context.hashCode() : 0; return "ALAudioSink[init "+initialized+", playRequested "+playRequested+", device "+deviceSpecifier+", ctx "+toHexString(ctxHash)+", alSource "+alSrcName+ ", chosen "+chosenFormat+ ", al[chan "+ALHelpers.alChannelLayoutName(alChannelLayout)+", type "+ALHelpers.alSampleTypeName(alSampleType)+ @@ -250,12 +250,12 @@ public class ALAudioSink implements AudioSink { "queued["+alFramesPlaying.size()+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes], "+ "queue[g "+frameGrowAmount+", l "+frameLimit+"]"; } - + public final String getPerfString() { final int alBuffersLen = null != alBufferNames ? alBufferNames.length : 0; return "Play [buffer "+alFramesPlaying.size()+"/"+alBuffersLen+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes]"; } - + @Override public final AudioFormat getPreferredFormat() { if( !staticAvailable ) { @@ -263,7 +263,7 @@ public class ALAudioSink implements AudioSink { } return preferredAudioFormat; } - + @Override public final int getMaxSupportedChannels() { if( !staticAvailable ) { @@ -271,14 +271,14 @@ public class ALAudioSink implements AudioSink { } return hasSOFTBufferSamples ? 8 : 2; } - + @Override public final boolean isSupported(AudioFormat format) { if( !staticAvailable ) { return false; } if( format.planar || !format.littleEndian ) { - // FIXME big-endian supported w/ SOFT where it's native format! + // FIXME big-endian supported w/ SOFT where it's native format! return false; } final int alChannelLayout = ALHelpers.getDefaultALChannelLayout(format.channelCount); @@ -286,7 +286,7 @@ public class ALAudioSink implements AudioSink { final int alSampleType = ALHelpers.getALSampleType(format.sampleSize, format.signed, format.fixedP); if( AL.AL_NONE != alSampleType ) { lockContext(); - try { + try { final int alFormat = ALHelpers.getALFormat(alChannelLayout, alSampleType, hasSOFTBufferSamples, al, alExt); return AL.AL_NONE != alFormat; } finally { @@ -296,7 +296,7 @@ public class ALAudioSink implements AudioSink { } return false; } - + @Override public final boolean init(AudioFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { if( !staticAvailable ) { @@ -318,10 +318,10 @@ public class ALAudioSink implements AudioSink { // Allocate buffers destroyBuffers(); { - final float useFrameDuration = frameDuration > 1f ? frameDuration : AudioSink.DefaultFrameDuration; + final float useFrameDuration = frameDuration > 1f ? frameDuration : AudioSink.DefaultFrameDuration; final int initialFrameCount = requestedFormat.getFrameCount( initialQueueSize > 0 ? initialQueueSize : AudioSink.DefaultInitialQueueSize, useFrameDuration); - // frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { + // frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { alBufferNames = new int[initialFrameCount]; al.alGenBuffers(initialFrameCount, alBufferNames, 0); final int err = al.alGetError(); @@ -333,7 +333,7 @@ public class ALAudioSink implements AudioSink { for(int i=0; i(alFrames); alFramesPlaying = new LFRingbuffer(ALAudioFrame[].class, initialFrameCount); this.frameGrowAmount = requestedFormat.getFrameCount( @@ -344,11 +344,11 @@ public class ALAudioSink implements AudioSink { } finally { unlockContext(); } - + chosenFormat = requestedFormat; return true; } - + private static int[] concat(int[] first, int[] second) { final int[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); @@ -360,7 +360,7 @@ public class ALAudioSink implements AudioSink { System.arraycopy(second, 0, result, first.length, second.length); return result; } */ - + private boolean growBuffers() { if( !alFramesAvail.isEmpty() || !alFramesPlaying.isFull() ) { throw new InternalError("Buffers: Avail is !empty "+alFramesAvail+" or Playing is !full "+alFramesPlaying); @@ -371,7 +371,7 @@ public class ALAudioSink implements AudioSink { } return false; } - + final int[] newALBufferNames = new int[frameGrowAmount]; al.alGenBuffers(frameGrowAmount, newALBufferNames, 0); final int err = al.alGetError(); @@ -382,7 +382,7 @@ public class ALAudioSink implements AudioSink { return false; } alBufferNames = concat(alBufferNames, newALBufferNames); - + final ALAudioFrame[] newALBuffers = new ALAudioFrame[frameGrowAmount]; for(int i=0; i 0 ) { + if( alBufferBytesQueued > 0 ) { final int releaseBufferLimes = Math.max(1, alFramesPlaying.size() / 4 ); final int[] val=new int[1]; int i=0; @@ -490,8 +488,8 @@ public class ALAudioSink implements AudioSink { final int avgBufferDura = chosenFormat.getBytesDuration( alBufferBytesQueued / alFramesPlaying.size() ); final int sleep = Math.max(2, Math.min(100, releaseBufferLimes * avgBufferDura)); if( DEBUG || true ) { - System.err.println(getThreadName()+": ALAudioSink: Dequeue.wait["+i+"]: avgBufferDura "+avgBufferDura+", releaseBufferLimes "+releaseBufferLimes+", sleep "+sleep+" ms, playImpl "+isPlayingImpl1()+", processed "+val[0]+", "+this); - } + System.err.println(getThreadName()+": ALAudioSink: Dequeue.wait["+i+"]: avgBufferDura "+avgBufferDura+", releaseBufferLimes "+releaseBufferLimes+", sleep "+sleep+" ms, playImpl "+(AL.AL_PLAYING == getSourceState())+", processed "+val[0]+", "+this); + } unlockContext(); try { Thread.sleep( sleep - 1 ); @@ -511,7 +509,7 @@ public class ALAudioSink implements AudioSink { al.alSourceUnqueueBuffers(alSource[0], releaseBufferCount, buffers, 0); alErr = al.alGetError(); if( AL.AL_NO_ERROR != alErr ) { - throw new RuntimeException("ALError "+toHexString(alErr)+" while dequeueing "+releaseBufferCount+" buffers. "+this); + throw new RuntimeException("ALError "+toHexString(alErr)+" while dequeueing "+releaseBufferCount+" buffers. "+this); } for ( int i=0; i