audio: Added SDL_AudioStreamFlush().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
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,17 +545,37 @@ extern DECLSPEC int SDLCALL SDL_AudioStreamPut(SDL_AudioStream *stream, const vo
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
+ * \sa SDL_AudioStreamGet
+ * \sa SDL_AudioStreamClear
+ * \sa SDL_FreeAudioStream
+ */
+extern DECLSPEC int SDLCALL SDL_AudioStreamFlush(SDL_AudioStream *stream);
+
+/**
* Clear any pending data in the stream without converting it
*
* \sa SDL_NewAudioStream
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 @@ SDL_NewAudioStream(const SDL_AudioFormat src_format,
}
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 @@ SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len)
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 @@ SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
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_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
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 @@ SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
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 @@ SDL_AudioStreamClear(SDL_AudioStream *stream)
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(int,SDL_AudioStreamGet,(SDL_AudioStream *a, void *b, int c),(a,b
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)