From d42b236ef139dcc8d8713535893c7870b55d420c Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
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/openal/av/ALAudioSink.java')

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<ALAudioFrame> alFramesAvail = null;
     private Ringbuffer<ALAudioFrame> 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<initialFrameCount; i++) {
                     alFrames[i] = new ALAudioFrame(alBufferNames[i]);
                 }
-                
+
                 alFramesAvail = new LFRingbuffer<ALAudioFrame>(alFrames);
                 alFramesPlaying = new LFRingbuffer<ALAudioFrame>(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<frameGrowAmount; i++) {
             newALBuffers[i] = new ALAudioFrame(newALBufferNames[i]);
@@ -399,7 +399,7 @@ public class ALAudioSink implements AudioSink {
         }
         return true;
     }
-    
+
     private void destroyBuffers() {
         if( !staticAvailable ) {
             return;
@@ -422,7 +422,7 @@ public class ALAudioSink implements AudioSink {
             alBufferNames = null;
         }
     }
-    
+
     @Override
     public final void destroy() {
         initialized = false;
@@ -445,7 +445,7 @@ public class ALAudioSink implements AudioSink {
                 }
                 alSource = null;
             }
-            
+
             destroyBuffers();
         } finally {
             destroyContext();
@@ -459,22 +459,20 @@ public class ALAudioSink implements AudioSink {
                     t.printStackTrace();
                 }
             }
-            device = null;            
+            device = null;
         }
         chosenFormat = null;
     }
-    
+
     @Override
     public final boolean isInitialized() {
         return initialized;
     }
-    
-    private final int dequeueBuffer(boolean flush, boolean wait) {
+
+    private final int dequeueBuffer(boolean wait) {
         int alErr = AL.AL_NO_ERROR;
         final int releaseBufferCount;
-        if( flush ) {
-            releaseBufferCount = alFramesPlaying.size();
-        } else if( alBufferBytesQueued > 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<releaseBufferCount; i++ ) {
                 final ALAudioFrame releasedBuffer = alFramesPlaying.get();
@@ -520,7 +518,7 @@ public class ALAudioSink implements AudioSink {
                 }
                 if( releasedBuffer.alBuffer != buffers[i] ) {
                     alFramesAvail.dump(System.err, "Avail-deq02-post");
-                    alFramesPlaying.dump(System.err, "Playi-deq02-post");                    
+                    alFramesPlaying.dump(System.err, "Playi-deq02-post");
                     throw new InternalError("Buffer name mismatch: dequeued: "+buffers[i]+", released "+releasedBuffer+", "+this);
                 }
                 alBufferBytesQueued -= releasedBuffer.getByteSize();
@@ -528,17 +526,27 @@ public class ALAudioSink implements AudioSink {
                     throw new InternalError("Internal Error: "+this);
                 }
             }
-            if( flush && ( !alFramesAvail.isFull() || !alFramesPlaying.isEmpty() ) ) {
-                alFramesAvail.dump(System.err, "Avail-deq03-post");
-                alFramesPlaying.dump(System.err, "Playi-deq03-post");
-                throw new InternalError("Flush failure: "+this);
-            }
         }
         return releaseBufferCount;
     }
-    
+    private final void dequeueForceAll() {
+        while ( !alFramesPlaying.isEmpty() ) {
+            final ALAudioFrame releasedBuffer = alFramesPlaying.get();
+            if( null == releasedBuffer ) {
+                throw new InternalError("Internal Error: "+this);
+            }
+            alBufferBytesQueued -= releasedBuffer.getByteSize();
+            if( !alFramesAvail.put(releasedBuffer) ) {
+                throw new InternalError("Internal Error: "+this);
+            }
+        }
+        if( 0 != alBufferBytesQueued ) {
+            throw new InternalError("Internal Error: "+this);
+        }
+    }
+
     private final int dequeueBuffer(boolean wait, int inPTS, int inDuration) {
-        final int dequeuedBufferCount = dequeueBuffer( false /* flush */, wait );        
+        final int dequeuedBufferCount = dequeueBuffer( wait );
         final ALAudioFrame currentBuffer = alFramesPlaying.peek();
         if( null != currentBuffer ) {
             playingPTS = currentBuffer.getPTS();
@@ -552,12 +560,12 @@ public class ALAudioSink implements AudioSink {
         }
         return dequeuedBufferCount;
     }
-    
+
     @Override
     public final AudioFrame enqueueData(AudioDataFrame audioDataFrame) {
         return enqueueData(audioDataFrame.getPTS(), audioDataFrame.getData(), audioDataFrame.getByteSize());
     }
-    
+
     @Override
     public final AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount) {
         if( !initialized || null == chosenFormat ) {
@@ -565,7 +573,7 @@ public class ALAudioSink implements AudioSink {
         }
         final ALAudioFrame alFrame;
         int alErr = AL.AL_NO_ERROR;
-            
+
         // OpenAL consumes buffers in the background
         // we first need to initialize the OpenAL buffers then
         // start continuous playback.
@@ -575,7 +583,7 @@ public class ALAudioSink implements AudioSink {
             if(al.alGetError() != AL.AL_NO_ERROR) {
                 throw new RuntimeException("ALError "+toHexString(alErr)+" while makeCurrent. "+this);
             }
-            
+
             final int duration = chosenFormat.getBytesDuration(byteCount);
             final boolean dequeueDone;
             if( alFramesAvail.isEmpty() ) {
@@ -592,7 +600,7 @@ public class ALAudioSink implements AudioSink {
                 final boolean wait = isPlayingImpl0() && alFramesAvail.isEmpty(); // possible if grow failed or already exceeds it's limit!
                 dequeueBuffer(wait, pts, duration);
             }
-            
+
             alFrame = alFramesAvail.get();
             if( null == alFrame ) {
                 alFramesAvail.dump(System.err, "Avail");
@@ -612,7 +620,7 @@ public class ALAudioSink implements AudioSink {
             } else {
                 al.alBufferData(alFrame.alBuffer, alFormat, bytes, byteCount, chosenFormat.sampleRate);
             }
-            
+
             final int[] alBufferNames = new int[] { alFrame.alBuffer };
             al.alSourceQueueBuffers(alSource[0], 1, alBufferNames, 0);
             alErr = al.alGetError();
@@ -621,7 +629,7 @@ public class ALAudioSink implements AudioSink {
             }
             alBufferBytesQueued += byteCount;
             enqueuedFrameCount++;
-            
+
             playImpl(); // continue playing, fixes issue where we ran out of enqueued data!
         } finally {
             unlockContext();
@@ -640,28 +648,28 @@ public class ALAudioSink implements AudioSink {
                 return isPlayingImpl0();
             } finally {
                 unlockContext();
-            }                            
+            }
         } else {
             return false;
         }
     }
     private final boolean isPlayingImpl0() {
         if( playRequested ) {
-            return isPlayingImpl1();
+            return AL.AL_PLAYING == getSourceState();
         } else {
             return false;
         }
     }
-    private final boolean isPlayingImpl1() {
+    private final int getSourceState() {
         final int[] val = new int[1];
         al.alGetSourcei(alSource[0], AL.AL_SOURCE_STATE, val, 0);
         final int alErr = al.alGetError();
         if(al.alGetError() != AL.AL_NO_ERROR) {
-            throw new RuntimeException("ALError "+toHexString(alErr)+" while querying isPlaying. "+this);
+            throw new RuntimeException("ALError "+toHexString(alErr)+" while querying SOURCE_STATE. "+this);
         }
-        return val[0] == AL.AL_PLAYING;
+        return val[0];
     }
-    
+
     @Override
     public final void play() {
         if( !initialized || null == chosenFormat ) {
@@ -672,22 +680,22 @@ public class ALAudioSink implements AudioSink {
         try {
             playImpl();
             if( DEBUG ) {
-                System.err.println(getThreadName()+": ALAudioSink: PLAY playImpl "+isPlayingImpl1()+", "+this);
-            }        
+                System.err.println(getThreadName()+": ALAudioSink: PLAY playImpl "+(AL.AL_PLAYING == getSourceState())+", "+this);
+            }
         } finally {
             unlockContext();
-        }                
+        }
     }
     private final void playImpl() {
-        if( playRequested && !isPlayingImpl1() ) {
+        if( playRequested && AL.AL_PLAYING != getSourceState() ) {
             al.alSourcePlay(alSource[0]);
             final int alErr = al.alGetError();
             if(al.alGetError() != AL.AL_NO_ERROR) {
                 throw new RuntimeException("ALError "+toHexString(alErr)+" while start playing. "+this);
             }
-        }        
+        }
     }
-    
+
     @Override
     public final void pause() {
         if( !initialized || null == chosenFormat ) {
@@ -698,8 +706,8 @@ public class ALAudioSink implements AudioSink {
             try {
                 pauseImpl();
                 if( DEBUG ) {
-                    System.err.println(getThreadName()+": ALAudioSink: PAUSE playImpl "+isPlayingImpl1()+", "+this);
-                }        
+                    System.err.println(getThreadName()+": ALAudioSink: PAUSE playImpl "+(AL.AL_PLAYING == getSourceState())+", "+this);
+                }
             } finally {
                 unlockContext();
             }
@@ -716,7 +724,7 @@ public class ALAudioSink implements AudioSink {
         }
     }
     private final void stopImpl() {
-        if( isPlayingImpl0() ) {
+        if( AL.AL_STOPPED != getSourceState() ) {
             playRequested = false;
             al.alSourceStop(alSource[0]);
             final int alErr = al.alGetError();
@@ -725,12 +733,12 @@ public class ALAudioSink implements AudioSink {
             }
         }
     }
-    
+
     @Override
     public final float getPlaySpeed() { return playSpeed; }
-    
+
     @Override
-    public final boolean setPlaySpeed(float rate) { 
+    public final boolean setPlaySpeed(float rate) {
         if( !initialized || null == chosenFormat ) {
             return false;
         }
@@ -739,22 +747,22 @@ public class ALAudioSink implements AudioSink {
             if( Math.abs(1.0f - rate) < 0.01f ) {
                 rate = 1.0f;
             }
-            if( 0.5f <= rate && rate <= 2.0f ) { // OpenAL limits 
+            if( 0.5f <= rate && rate <= 2.0f ) { // OpenAL limits
                 playSpeed = rate;
                 al.alSourcef(alSource[0], AL.AL_PITCH, playSpeed);
                 return true;
-            } 
+            }
         } finally {
             unlockContext();
         }
-        return false; 
+        return false;
     }
-    
+
     @Override
     public final float getVolume() {
-        return volume;        
+        return volume;
     }
-    
+
     @Override
     public final boolean setVolume(float v) {
         if( !initialized || null == chosenFormat ) {
@@ -767,17 +775,17 @@ public class ALAudioSink implements AudioSink {
             } else if( Math.abs(1.0f - v) < 0.01f ) {
                 v = 1.0f;
             }
-            if( 0.0f <= v && v <= 1.0f ) { // OpenAL limits 
+            if( 0.0f <= v && v <= 1.0f ) { // OpenAL limits
                 volume = v;
                 al.alSourcef(alSource[0], AL.AL_GAIN, v);
                 return true;
-            } 
+            }
         } finally {
             unlockContext();
         }
-        return false; 
+        return false;
     }
-    
+
     @Override
     public final void flush() {
         if( !initialized || null == chosenFormat ) {
@@ -787,28 +795,30 @@ public class ALAudioSink implements AudioSink {
         try {
             // pauseImpl();
             stopImpl();
-            dequeueBuffer( true /* flush */, false /* wait */ );
+            al.alSourcei(alSource[0], AL.AL_BUFFER, 0); // explicit force zero buffer!
+            dequeueBuffer( false /* wait */ );
+            dequeueForceAll();
             if( alBufferNames.length != alFramesAvail.size() || alFramesPlaying.size() != 0 ) {
                 throw new InternalError("XXX: "+this);
             }
             if( DEBUG ) {
-                System.err.println(getThreadName()+": ALAudioSink: FLUSH playImpl "+isPlayingImpl1()+", "+this);
-            }        
+                System.err.println(getThreadName()+": ALAudioSink: FLUSH playImpl "+(AL.AL_PLAYING == getSourceState())+", "+this);
+            }
         } finally {
             unlockContext();
-        }                
+        }
     }
-    
+
     @Override
     public final int getEnqueuedFrameCount() {
         return enqueuedFrameCount;
     }
-    
+
     @Override
     public final int getFrameCount() {
         return null != alBufferNames ? alBufferNames.length : 0;
     }
-    
+
     @Override
     public final int getQueuedFrameCount() {
         if( !initialized || null == chosenFormat ) {
@@ -816,7 +826,7 @@ public class ALAudioSink implements AudioSink {
         }
         return alFramesPlaying.size();
     }
-    
+
     @Override
     public final int getFreeFrameCount() {
         if( !initialized || null == chosenFormat ) {
@@ -824,7 +834,7 @@ public class ALAudioSink implements AudioSink {
         }
         return alFramesAvail.size();
     }
-    
+
     @Override
     public final int getQueuedByteCount() {
         if( !initialized || null == chosenFormat ) {
@@ -832,7 +842,7 @@ public class ALAudioSink implements AudioSink {
         }
         return alBufferBytesQueued;
     }
-    
+
     @Override
     public final int getQueuedTime() {
         if( !initialized || null == chosenFormat ) {
@@ -840,10 +850,10 @@ public class ALAudioSink implements AudioSink {
         }
         return chosenFormat.getBytesDuration(alBufferBytesQueued);
     }
-    
+
     @Override
     public final int getPTS() { return playingPTS; }
-    
+
     private static final String toHexString(int v) { return "0x"+Integer.toHexString(v); }
-    private static final String getThreadName() { return Thread.currentThread().getName(); }        
+    private static final String getThreadName() { return Thread.currentThread().getName(); }
 }
-- 
cgit v1.2.3