audio: Added SDL_AudioStreamFlush().

--HG--
extra : rebase_source : 5bd3136ca1bcda9dcde4b2f6ab4891789e83331b
diff --git a/include/SDL_audio.h b/include/SDL_audio.h
index b30c409..a242ace 100644
--- a/include/SDL_audio.h
+++ b/include/SDL_audio.h
@@ -545,7 +545,27 @@
 extern DECLSPEC int SDLCALL SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len);
 
 /**
- * Get the number of converted/resampled bytes available
+ * Get the number of converted/resampled bytes available. The stream may be
+ *  buffering data behind the scenes until it has enough to resample
+ *  correctly, so this number might be lower than what you expect, or even
+ *  be zero. Add more data or flush the stream if you need the data now.
+ *
+ *  \sa SDL_NewAudioStream
+ *  \sa SDL_AudioStreamPut
+ *  \sa SDL_AudioStreamGet
+ *  \sa SDL_AudioStreamClear
+ *  \sa SDL_AudioStreamFlush
+ *  \sa SDL_FreeAudioStream
+ */
+extern DECLSPEC int SDLCALL SDL_AudioStreamAvailable(SDL_AudioStream *stream);
+
+/**
+ * Tell the stream that you're done sending data, and anything being buffered
+ *  should be converted/resampled and made available immediately.
+ *
+ * It is legal to add more data to a stream after flushing, but there will
+ *  be audio gaps in the output. Generally this is intended to signal the
+ *  end of input, so the complete output becomes available.
  *
  *  \sa SDL_NewAudioStream
  *  \sa SDL_AudioStreamPut
@@ -553,7 +573,7 @@
  *  \sa SDL_AudioStreamClear
  *  \sa SDL_FreeAudioStream
  */
-extern DECLSPEC int SDLCALL SDL_AudioStreamAvailable(SDL_AudioStream *stream);
+extern DECLSPEC int SDLCALL SDL_AudioStreamFlush(SDL_AudioStream *stream);
 
 /**
  *  Clear any pending data in the stream without converting it
diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c
index 3a10fc9..9dba980 100644
--- a/src/audio/SDL_audiocvt.c
+++ b/src/audio/SDL_audiocvt.c
@@ -1362,7 +1362,7 @@
 }
 
 static int
-SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len)
+SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len, int *maxputbytes)
 {
     int buflen = len;
     int workbuflen;
@@ -1479,6 +1479,13 @@
     printf("AUDIOSTREAM: Final output is %d bytes\n", buflen);
     #endif
 
+    if (maxputbytes) {
+        const int maxbytes = *maxputbytes;
+        if (buflen > maxbytes)
+            buflen = maxbytes;
+        *maxputbytes -= buflen;
+    }
+
     /* resamplebuf holds the final output, even if we didn't resample. */
     return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0;
 }
@@ -1524,7 +1531,7 @@
            we don't need to store it for later, skip the staging process.
          */
         if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) {
-            return SDL_AudioStreamPutInternal(stream, buf, len);
+            return SDL_AudioStreamPutInternal(stream, buf, len, NULL);
         }
 
         /* If there's not enough data to fill the staging buffer, just save it */
@@ -1539,7 +1546,7 @@
         SDL_assert(amount > 0);
         SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount);
         stream->staging_buffer_filled = 0;
-        if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size) < 0) {
+        if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, NULL) < 0) {
             return -1;
         }
         buf = (void *)((Uint8 *)buf + amount);
@@ -1548,6 +1555,58 @@
     return 0;
 }
 
+int SDL_AudioStreamFlush(SDL_AudioStream *stream)
+{
+    if (!stream) {
+        return SDL_InvalidParamError("stream");
+    }
+
+    #if DEBUG_AUDIOSTREAM
+    printf("AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes\n", stream->staging_buffer_filled);
+    #endif
+
+    /* shouldn't use a staging buffer if we're not resampling. */
+    SDL_assert((stream->dst_rate != stream->src_rate) || (stream->staging_buffer_filled == 0));
+
+    if (stream->staging_buffer_filled > 0) {
+        /* push the staging buffer + silence. We need to flush out not just
+           the staging buffer, but the piece that the stream was saving off
+           for right-side resampler padding. */
+        const SDL_bool first_run = stream->first_run;
+        const int filled = stream->staging_buffer_filled;
+        int actual_input_frames = filled / stream->src_sample_frame_size;
+        if (!first_run)
+            actual_input_frames += stream->resampler_padding_samples / stream->pre_resample_channels;
+
+        if (actual_input_frames > 0) {  /* don't bother if nothing to flush. */
+            /* This is how many bytes we're expecting without silence appended. */
+            int flush_remaining = ((int) SDL_ceil(actual_input_frames * stream->rate_incr)) * stream->dst_sample_frame_size;
+
+            #if DEBUG_AUDIOSTREAM
+            printf("AUDIOSTREAM: flushing with padding to get max %d bytes!\n", flush_remaining);
+            #endif
+
+            SDL_memset(stream->staging_buffer + filled, '\0', stream->staging_buffer_size - filled);
+            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
+                return -1;
+            }
+
+            /* we have flushed out (or initially filled) the pending right-side
+               resampler padding, but we need to push more silence to guarantee
+               the staging buffer is fully flushed out, too. */
+            SDL_memset(stream->staging_buffer, '\0', filled);
+            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
+                return -1;
+            }
+        }
+    }
+
+    stream->staging_buffer_filled = 0;
+    stream->first_run = SDL_TRUE;
+
+    return 0;
+}
+
 /* get converted/resampled data from the stream */
 int
 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len)
@@ -1587,6 +1646,7 @@
             stream->reset_resampler_func(stream);
         }
         stream->first_run = SDL_TRUE;
+        stream->staging_buffer_filled = 0;
     }
 }
 
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 0770512..ac3980f 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -646,3 +646,4 @@
 #define SDL_AudioStreamClear SDL_AudioStreamClear_REAL
 #define SDL_AudioStreamAvailable SDL_AudioStreamAvailable_REAL
 #define SDL_FreeAudioStream SDL_FreeAudioStream_REAL
+#define SDL_AudioStreamFlush SDL_AudioStreamFlush_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index e1372ef..8c0f8b7 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -680,3 +680,4 @@
 SDL_DYNAPI_PROC(void,SDL_AudioStreamClear,(SDL_AudioStream *a),(a),)
 SDL_DYNAPI_PROC(int,SDL_AudioStreamAvailable,(SDL_AudioStream *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_FreeAudioStream,(SDL_AudioStream *a),(a),)
+SDL_DYNAPI_PROC(int,SDL_AudioStreamFlush,(SDL_AudioStream *a),(a),return)