1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | #include "../SDL_internal.h" |
22 | |
23 | /* Functions for audio drivers to perform runtime conversion of audio format */ |
24 | |
25 | /* FIXME: Channel weights when converting from more channels to fewer may need to be adjusted, see https://msdn.microsoft.com/en-us/library/windows/desktop/ff819070(v=vs.85).aspx |
26 | */ |
27 | |
28 | #include "SDL.h" |
29 | #include "SDL_audio.h" |
30 | #include "SDL_audio_c.h" |
31 | |
32 | #include "SDL_loadso.h" |
33 | #include "../SDL_dataqueue.h" |
34 | #include "SDL_cpuinfo.h" |
35 | |
36 | #define DEBUG_AUDIOSTREAM 0 |
37 | |
38 | #ifdef __SSE3__ |
39 | #define HAVE_SSE3_INTRINSICS 1 |
40 | #endif |
41 | |
42 | #if HAVE_SSE3_INTRINSICS |
43 | /* Convert from stereo to mono. Average left and right. */ |
44 | static void SDLCALL |
45 | SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
46 | { |
47 | float *dst = (float *) cvt->buf; |
48 | const float *src = dst; |
49 | int i = cvt->len_cvt / 8; |
50 | |
51 | LOG_DEBUG_CONVERT("stereo" , "mono (using SSE3)" ); |
52 | SDL_assert(format == AUDIO_F32SYS); |
53 | |
54 | /* We can only do this if dst is aligned to 16 bytes; since src is the |
55 | same pointer and it moves by 2, it can't be forcibly aligned. */ |
56 | if ((((size_t) dst) & 15) == 0) { |
57 | /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ |
58 | const __m128 divby2 = _mm_set1_ps(0.5f); |
59 | while (i >= 4) { /* 4 * float32 */ |
60 | _mm_store_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_load_ps(src), _mm_load_ps(src+4)), divby2)); |
61 | i -= 4; src += 8; dst += 4; |
62 | } |
63 | } |
64 | |
65 | /* Finish off any leftovers with scalar operations. */ |
66 | while (i) { |
67 | *dst = (src[0] + src[1]) * 0.5f; |
68 | dst++; i--; src += 2; |
69 | } |
70 | |
71 | cvt->len_cvt /= 2; |
72 | if (cvt->filters[++cvt->filter_index]) { |
73 | cvt->filters[cvt->filter_index] (cvt, format); |
74 | } |
75 | } |
76 | #endif |
77 | |
78 | /* Convert from stereo to mono. Average left and right. */ |
79 | static void SDLCALL |
80 | SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
81 | { |
82 | float *dst = (float *) cvt->buf; |
83 | const float *src = dst; |
84 | int i; |
85 | |
86 | LOG_DEBUG_CONVERT("stereo" , "mono" ); |
87 | SDL_assert(format == AUDIO_F32SYS); |
88 | |
89 | for (i = cvt->len_cvt / 8; i; --i, src += 2) { |
90 | *(dst++) = (src[0] + src[1]) * 0.5f; |
91 | } |
92 | |
93 | cvt->len_cvt /= 2; |
94 | if (cvt->filters[++cvt->filter_index]) { |
95 | cvt->filters[cvt->filter_index] (cvt, format); |
96 | } |
97 | } |
98 | |
99 | |
100 | /* Convert from 5.1 to stereo. Average left and right, distribute center, discard LFE. */ |
101 | static void SDLCALL |
102 | SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
103 | { |
104 | float *dst = (float *) cvt->buf; |
105 | const float *src = dst; |
106 | int i; |
107 | |
108 | LOG_DEBUG_CONVERT("5.1" , "stereo" ); |
109 | SDL_assert(format == AUDIO_F32SYS); |
110 | |
111 | /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */ |
112 | for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) { |
113 | const float front_center_distributed = src[2] * 0.5f; |
114 | dst[0] = (src[0] + front_center_distributed + src[4]) / 2.5f; /* left */ |
115 | dst[1] = (src[1] + front_center_distributed + src[5]) / 2.5f; /* right */ |
116 | } |
117 | |
118 | cvt->len_cvt /= 3; |
119 | if (cvt->filters[++cvt->filter_index]) { |
120 | cvt->filters[cvt->filter_index] (cvt, format); |
121 | } |
122 | } |
123 | |
124 | |
125 | /* Convert from quad to stereo. Average left and right. */ |
126 | static void SDLCALL |
127 | SDL_ConvertQuadToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
128 | { |
129 | float *dst = (float *) cvt->buf; |
130 | const float *src = dst; |
131 | int i; |
132 | |
133 | LOG_DEBUG_CONVERT("quad" , "stereo" ); |
134 | SDL_assert(format == AUDIO_F32SYS); |
135 | |
136 | for (i = cvt->len_cvt / (sizeof (float) * 4); i; --i, src += 4, dst += 2) { |
137 | dst[0] = (src[0] + src[2]) * 0.5f; /* left */ |
138 | dst[1] = (src[1] + src[3]) * 0.5f; /* right */ |
139 | } |
140 | |
141 | cvt->len_cvt /= 2; |
142 | if (cvt->filters[++cvt->filter_index]) { |
143 | cvt->filters[cvt->filter_index] (cvt, format); |
144 | } |
145 | } |
146 | |
147 | |
148 | /* Convert from 7.1 to 5.1. Distribute sides across front and back. */ |
149 | static void SDLCALL |
150 | SDL_Convert71To51(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
151 | { |
152 | float *dst = (float *) cvt->buf; |
153 | const float *src = dst; |
154 | int i; |
155 | |
156 | LOG_DEBUG_CONVERT("7.1" , "5.1" ); |
157 | SDL_assert(format == AUDIO_F32SYS); |
158 | |
159 | for (i = cvt->len_cvt / (sizeof (float) * 8); i; --i, src += 8, dst += 6) { |
160 | const float surround_left_distributed = src[6] * 0.5f; |
161 | const float surround_right_distributed = src[7] * 0.5f; |
162 | dst[0] = (src[0] + surround_left_distributed) / 1.5f; /* FL */ |
163 | dst[1] = (src[1] + surround_right_distributed) / 1.5f; /* FR */ |
164 | dst[2] = src[2] / 1.5f; /* CC */ |
165 | dst[3] = src[3] / 1.5f; /* LFE */ |
166 | dst[4] = (src[4] + surround_left_distributed) / 1.5f; /* BL */ |
167 | dst[5] = (src[5] + surround_right_distributed) / 1.5f; /* BR */ |
168 | } |
169 | |
170 | cvt->len_cvt /= 8; |
171 | cvt->len_cvt *= 6; |
172 | if (cvt->filters[++cvt->filter_index]) { |
173 | cvt->filters[cvt->filter_index] (cvt, format); |
174 | } |
175 | } |
176 | |
177 | |
178 | /* Convert from 5.1 to quad. Distribute center across front, discard LFE. */ |
179 | static void SDLCALL |
180 | SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
181 | { |
182 | float *dst = (float *) cvt->buf; |
183 | const float *src = dst; |
184 | int i; |
185 | |
186 | LOG_DEBUG_CONVERT("5.1" , "quad" ); |
187 | SDL_assert(format == AUDIO_F32SYS); |
188 | |
189 | /* SDL's 4.0 layout: FL+FR+BL+BR */ |
190 | /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */ |
191 | for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) { |
192 | const float front_center_distributed = src[2] * 0.5f; |
193 | dst[0] = (src[0] + front_center_distributed) / 1.5f; /* FL */ |
194 | dst[1] = (src[1] + front_center_distributed) / 1.5f; /* FR */ |
195 | dst[2] = src[4] / 1.5f; /* BL */ |
196 | dst[3] = src[5] / 1.5f; /* BR */ |
197 | } |
198 | |
199 | cvt->len_cvt /= 6; |
200 | cvt->len_cvt *= 4; |
201 | if (cvt->filters[++cvt->filter_index]) { |
202 | cvt->filters[cvt->filter_index] (cvt, format); |
203 | } |
204 | } |
205 | |
206 | |
207 | /* Upmix mono to stereo (by duplication) */ |
208 | static void SDLCALL |
209 | SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
210 | { |
211 | const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
212 | float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); |
213 | int i; |
214 | |
215 | LOG_DEBUG_CONVERT("mono" , "stereo" ); |
216 | SDL_assert(format == AUDIO_F32SYS); |
217 | |
218 | for (i = cvt->len_cvt / sizeof (float); i; --i) { |
219 | src--; |
220 | dst -= 2; |
221 | dst[0] = dst[1] = *src; |
222 | } |
223 | |
224 | cvt->len_cvt *= 2; |
225 | if (cvt->filters[++cvt->filter_index]) { |
226 | cvt->filters[cvt->filter_index] (cvt, format); |
227 | } |
228 | } |
229 | |
230 | |
231 | /* Upmix stereo to a pseudo-5.1 stream */ |
232 | static void SDLCALL |
233 | SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
234 | { |
235 | int i; |
236 | float lf, rf, ce; |
237 | const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
238 | float *dst = (float *) (cvt->buf + cvt->len_cvt * 3); |
239 | |
240 | LOG_DEBUG_CONVERT("stereo" , "5.1" ); |
241 | SDL_assert(format == AUDIO_F32SYS); |
242 | |
243 | for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) { |
244 | dst -= 6; |
245 | src -= 2; |
246 | lf = src[0]; |
247 | rf = src[1]; |
248 | ce = (lf + rf) * 0.5f; |
249 | /* !!! FIXME: FL and FR may clip */ |
250 | dst[0] = lf + (lf - ce); /* FL */ |
251 | dst[1] = rf + (rf - ce); /* FR */ |
252 | dst[2] = ce; /* FC */ |
253 | dst[3] = 0; /* LFE (only meant for special LFE effects) */ |
254 | dst[4] = lf; /* BL */ |
255 | dst[5] = rf; /* BR */ |
256 | } |
257 | |
258 | cvt->len_cvt *= 3; |
259 | if (cvt->filters[++cvt->filter_index]) { |
260 | cvt->filters[cvt->filter_index] (cvt, format); |
261 | } |
262 | } |
263 | |
264 | |
265 | /* Upmix quad to a pseudo-5.1 stream */ |
266 | static void SDLCALL |
267 | SDL_ConvertQuadTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
268 | { |
269 | int i; |
270 | float lf, rf, lb, rb, ce; |
271 | const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
272 | float *dst = (float *) (cvt->buf + cvt->len_cvt * 3 / 2); |
273 | |
274 | LOG_DEBUG_CONVERT("quad" , "5.1" ); |
275 | SDL_assert(format == AUDIO_F32SYS); |
276 | SDL_assert(cvt->len_cvt % (sizeof(float) * 4) == 0); |
277 | |
278 | for (i = cvt->len_cvt / (sizeof(float) * 4); i; --i) { |
279 | dst -= 6; |
280 | src -= 4; |
281 | lf = src[0]; |
282 | rf = src[1]; |
283 | lb = src[2]; |
284 | rb = src[3]; |
285 | ce = (lf + rf) * 0.5f; |
286 | /* !!! FIXME: FL and FR may clip */ |
287 | dst[0] = lf + (lf - ce); /* FL */ |
288 | dst[1] = rf + (rf - ce); /* FR */ |
289 | dst[2] = ce; /* FC */ |
290 | dst[3] = 0; /* LFE (only meant for special LFE effects) */ |
291 | dst[4] = lb; /* BL */ |
292 | dst[5] = rb; /* BR */ |
293 | } |
294 | |
295 | cvt->len_cvt = cvt->len_cvt * 3 / 2; |
296 | if (cvt->filters[++cvt->filter_index]) { |
297 | cvt->filters[cvt->filter_index] (cvt, format); |
298 | } |
299 | } |
300 | |
301 | |
302 | /* Upmix stereo to a pseudo-4.0 stream (by duplication) */ |
303 | static void SDLCALL |
304 | SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
305 | { |
306 | const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
307 | float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); |
308 | float lf, rf; |
309 | int i; |
310 | |
311 | LOG_DEBUG_CONVERT("stereo" , "quad" ); |
312 | SDL_assert(format == AUDIO_F32SYS); |
313 | |
314 | for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) { |
315 | dst -= 4; |
316 | src -= 2; |
317 | lf = src[0]; |
318 | rf = src[1]; |
319 | dst[0] = lf; /* FL */ |
320 | dst[1] = rf; /* FR */ |
321 | dst[2] = lf; /* BL */ |
322 | dst[3] = rf; /* BR */ |
323 | } |
324 | |
325 | cvt->len_cvt *= 2; |
326 | if (cvt->filters[++cvt->filter_index]) { |
327 | cvt->filters[cvt->filter_index] (cvt, format); |
328 | } |
329 | } |
330 | |
331 | |
332 | /* Upmix 5.1 to 7.1 */ |
333 | static void SDLCALL |
334 | SDL_Convert51To71(SDL_AudioCVT * cvt, SDL_AudioFormat format) |
335 | { |
336 | float lf, rf, lb, rb, ls, rs; |
337 | int i; |
338 | const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
339 | float *dst = (float *) (cvt->buf + cvt->len_cvt * 4 / 3); |
340 | |
341 | LOG_DEBUG_CONVERT("5.1" , "7.1" ); |
342 | SDL_assert(format == AUDIO_F32SYS); |
343 | SDL_assert(cvt->len_cvt % (sizeof(float) * 6) == 0); |
344 | |
345 | for (i = cvt->len_cvt / (sizeof(float) * 6); i; --i) { |
346 | dst -= 8; |
347 | src -= 6; |
348 | lf = src[0]; |
349 | rf = src[1]; |
350 | lb = src[4]; |
351 | rb = src[5]; |
352 | ls = (lf + lb) * 0.5f; |
353 | rs = (rf + rb) * 0.5f; |
354 | /* !!! FIXME: these four may clip */ |
355 | lf += lf - ls; |
356 | rf += rf - ls; |
357 | lb += lb - ls; |
358 | rb += rb - ls; |
359 | dst[3] = src[3]; /* LFE */ |
360 | dst[2] = src[2]; /* FC */ |
361 | dst[7] = rs; /* SR */ |
362 | dst[6] = ls; /* SL */ |
363 | dst[5] = rb; /* BR */ |
364 | dst[4] = lb; /* BL */ |
365 | dst[1] = rf; /* FR */ |
366 | dst[0] = lf; /* FL */ |
367 | } |
368 | |
369 | cvt->len_cvt = cvt->len_cvt * 4 / 3; |
370 | |
371 | if (cvt->filters[++cvt->filter_index]) { |
372 | cvt->filters[cvt->filter_index] (cvt, format); |
373 | } |
374 | } |
375 | |
376 | /* SDL's resampler uses a "bandlimited interpolation" algorithm: |
377 | https://ccrma.stanford.edu/~jos/resample/ */ |
378 | |
379 | #define RESAMPLER_ZERO_CROSSINGS 5 |
380 | #define RESAMPLER_BITS_PER_SAMPLE 16 |
381 | #define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)) |
382 | #define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1) |
383 | |
384 | /* This is a "modified" bessel function, so you can't use POSIX j0() */ |
385 | static double |
386 | bessel(const double x) |
387 | { |
388 | const double xdiv2 = x / 2.0; |
389 | double i0 = 1.0f; |
390 | double f = 1.0f; |
391 | int i = 1; |
392 | |
393 | while (SDL_TRUE) { |
394 | const double diff = SDL_pow(xdiv2, i * 2) / SDL_pow(f, 2); |
395 | if (diff < 1.0e-21f) { |
396 | break; |
397 | } |
398 | i0 += diff; |
399 | i++; |
400 | f *= (double) i; |
401 | } |
402 | |
403 | return i0; |
404 | } |
405 | |
406 | /* build kaiser table with cardinal sine applied to it, and array of differences between elements. */ |
407 | static void |
408 | kaiser_and_sinc(float *table, float *diffs, const int tablelen, const double beta) |
409 | { |
410 | const int lenm1 = tablelen - 1; |
411 | const int lenm1div2 = lenm1 / 2; |
412 | int i; |
413 | |
414 | table[0] = 1.0f; |
415 | for (i = 1; i < tablelen; i++) { |
416 | const double kaiser = bessel(beta * SDL_sqrt(1.0 - SDL_pow(((i - lenm1) / 2.0) / lenm1div2, 2.0))) / bessel(beta); |
417 | table[tablelen - i] = (float) kaiser; |
418 | } |
419 | |
420 | for (i = 1; i < tablelen; i++) { |
421 | const float x = (((float) i) / ((float) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * ((float) M_PI); |
422 | table[i] *= SDL_sinf(x) / x; |
423 | diffs[i - 1] = table[i] - table[i - 1]; |
424 | } |
425 | diffs[lenm1] = 0.0f; |
426 | } |
427 | |
428 | |
429 | static SDL_SpinLock ResampleFilterSpinlock = 0; |
430 | static float *ResamplerFilter = NULL; |
431 | static float *ResamplerFilterDifference = NULL; |
432 | |
433 | int |
434 | SDL_PrepareResampleFilter(void) |
435 | { |
436 | SDL_AtomicLock(&ResampleFilterSpinlock); |
437 | if (!ResamplerFilter) { |
438 | /* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */ |
439 | const double dB = 80.0; |
440 | const double beta = 0.1102 * (dB - 8.7); |
441 | const size_t alloclen = RESAMPLER_FILTER_SIZE * sizeof (float); |
442 | |
443 | ResamplerFilter = (float *) SDL_malloc(alloclen); |
444 | if (!ResamplerFilter) { |
445 | SDL_AtomicUnlock(&ResampleFilterSpinlock); |
446 | return SDL_OutOfMemory(); |
447 | } |
448 | |
449 | ResamplerFilterDifference = (float *) SDL_malloc(alloclen); |
450 | if (!ResamplerFilterDifference) { |
451 | SDL_free(ResamplerFilter); |
452 | ResamplerFilter = NULL; |
453 | SDL_AtomicUnlock(&ResampleFilterSpinlock); |
454 | return SDL_OutOfMemory(); |
455 | } |
456 | kaiser_and_sinc(ResamplerFilter, ResamplerFilterDifference, RESAMPLER_FILTER_SIZE, beta); |
457 | } |
458 | SDL_AtomicUnlock(&ResampleFilterSpinlock); |
459 | return 0; |
460 | } |
461 | |
462 | void |
463 | SDL_FreeResampleFilter(void) |
464 | { |
465 | SDL_free(ResamplerFilter); |
466 | SDL_free(ResamplerFilterDifference); |
467 | ResamplerFilter = NULL; |
468 | ResamplerFilterDifference = NULL; |
469 | } |
470 | |
471 | static int |
472 | ResamplerPadding(const int inrate, const int outrate) |
473 | { |
474 | if (inrate == outrate) { |
475 | return 0; |
476 | } else if (inrate > outrate) { |
477 | return (int) SDL_ceil(((float) (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) / ((float) outrate))); |
478 | } |
479 | return RESAMPLER_SAMPLES_PER_ZERO_CROSSING; |
480 | } |
481 | |
482 | /* lpadding and rpadding are expected to be buffers of (ResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes. */ |
483 | static int |
484 | SDL_ResampleAudio(const int chans, const int inrate, const int outrate, |
485 | const float *lpadding, const float *rpadding, |
486 | const float *inbuf, const int inbuflen, |
487 | float *outbuf, const int outbuflen) |
488 | { |
489 | const double finrate = (double) inrate; |
490 | const double outtimeincr = 1.0 / ((float) outrate); |
491 | const double ratio = ((float) outrate) / ((float) inrate); |
492 | const int paddinglen = ResamplerPadding(inrate, outrate); |
493 | const int framelen = chans * (int)sizeof (float); |
494 | const int inframes = inbuflen / framelen; |
495 | const int wantedoutframes = (int) ((inbuflen / framelen) * ratio); /* outbuflen isn't total to write, it's total available. */ |
496 | const int maxoutframes = outbuflen / framelen; |
497 | const int outframes = SDL_min(wantedoutframes, maxoutframes); |
498 | float *dst = outbuf; |
499 | double outtime = 0.0; |
500 | int i, j, chan; |
501 | |
502 | for (i = 0; i < outframes; i++) { |
503 | const int srcindex = (int) (outtime * inrate); |
504 | const double intime = ((double) srcindex) / finrate; |
505 | const double innexttime = ((double) (srcindex + 1)) / finrate; |
506 | const double interpolation1 = 1.0 - ((innexttime - outtime) / (innexttime - intime)); |
507 | const int filterindex1 = (int) (interpolation1 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING); |
508 | const double interpolation2 = 1.0 - interpolation1; |
509 | const int filterindex2 = (int) (interpolation2 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING); |
510 | |
511 | for (chan = 0; chan < chans; chan++) { |
512 | float outsample = 0.0f; |
513 | |
514 | /* do this twice to calculate the sample, once for the "left wing" and then same for the right. */ |
515 | /* !!! FIXME: do both wings in one loop */ |
516 | for (j = 0; (filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) { |
517 | const int srcframe = srcindex - j; |
518 | /* !!! FIXME: we can bubble this conditional out of here by doing a pre loop. */ |
519 | const float insample = (srcframe < 0) ? lpadding[((paddinglen + srcframe) * chans) + chan] : inbuf[(srcframe * chans) + chan]; |
520 | outsample += (float)(insample * (ResamplerFilter[filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)] + (interpolation1 * ResamplerFilterDifference[filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)]))); |
521 | } |
522 | |
523 | for (j = 0; (filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) { |
524 | const int srcframe = srcindex + 1 + j; |
525 | /* !!! FIXME: we can bubble this conditional out of here by doing a post loop. */ |
526 | const float insample = (srcframe >= inframes) ? rpadding[((srcframe - inframes) * chans) + chan] : inbuf[(srcframe * chans) + chan]; |
527 | outsample += (float)(insample * (ResamplerFilter[filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)] + (interpolation2 * ResamplerFilterDifference[filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)]))); |
528 | } |
529 | *(dst++) = outsample; |
530 | } |
531 | |
532 | outtime += outtimeincr; |
533 | } |
534 | |
535 | return outframes * chans * sizeof (float); |
536 | } |
537 | |
538 | int |
539 | SDL_ConvertAudio(SDL_AudioCVT * cvt) |
540 | { |
541 | /* !!! FIXME: (cvt) should be const; stack-copy it here. */ |
542 | /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */ |
543 | |
544 | /* Make sure there's data to convert */ |
545 | if (cvt->buf == NULL) { |
546 | return SDL_SetError("No buffer allocated for conversion" ); |
547 | } |
548 | |
549 | /* Return okay if no conversion is necessary */ |
550 | cvt->len_cvt = cvt->len; |
551 | if (cvt->filters[0] == NULL) { |
552 | return 0; |
553 | } |
554 | |
555 | /* Set up the conversion and go! */ |
556 | cvt->filter_index = 0; |
557 | cvt->filters[0] (cvt, cvt->src_format); |
558 | return 0; |
559 | } |
560 | |
561 | static void SDLCALL |
562 | SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format) |
563 | { |
564 | #if DEBUG_CONVERT |
565 | printf("Converting byte order\n" ); |
566 | #endif |
567 | |
568 | switch (SDL_AUDIO_BITSIZE(format)) { |
569 | #define CASESWAP(b) \ |
570 | case b: { \ |
571 | Uint##b *ptr = (Uint##b *) cvt->buf; \ |
572 | int i; \ |
573 | for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \ |
574 | *ptr = SDL_Swap##b(*ptr); \ |
575 | } \ |
576 | break; \ |
577 | } |
578 | |
579 | CASESWAP(16); |
580 | CASESWAP(32); |
581 | CASESWAP(64); |
582 | |
583 | #undef CASESWAP |
584 | |
585 | default: SDL_assert(!"unhandled byteswap datatype!" ); break; |
586 | } |
587 | |
588 | if (cvt->filters[++cvt->filter_index]) { |
589 | /* flip endian flag for data. */ |
590 | if (format & SDL_AUDIO_MASK_ENDIAN) { |
591 | format &= ~SDL_AUDIO_MASK_ENDIAN; |
592 | } else { |
593 | format |= SDL_AUDIO_MASK_ENDIAN; |
594 | } |
595 | cvt->filters[cvt->filter_index](cvt, format); |
596 | } |
597 | } |
598 | |
599 | static int |
600 | SDL_AddAudioCVTFilter(SDL_AudioCVT *cvt, const SDL_AudioFilter filter) |
601 | { |
602 | if (cvt->filter_index >= SDL_AUDIOCVT_MAX_FILTERS) { |
603 | return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d" , SDL_AUDIOCVT_MAX_FILTERS); |
604 | } |
605 | if (filter == NULL) { |
606 | return SDL_SetError("Audio filter pointer is NULL" ); |
607 | } |
608 | cvt->filters[cvt->filter_index++] = filter; |
609 | cvt->filters[cvt->filter_index] = NULL; /* Moving terminator */ |
610 | return 0; |
611 | } |
612 | |
613 | static int |
614 | SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt) |
615 | { |
616 | int retval = 0; /* 0 == no conversion necessary. */ |
617 | |
618 | if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) { |
619 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { |
620 | return -1; |
621 | } |
622 | retval = 1; /* added a converter. */ |
623 | } |
624 | |
625 | if (!SDL_AUDIO_ISFLOAT(src_fmt)) { |
626 | const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt); |
627 | const Uint16 dst_bitsize = 32; |
628 | SDL_AudioFilter filter = NULL; |
629 | |
630 | switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) { |
631 | case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break; |
632 | case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break; |
633 | case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break; |
634 | case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break; |
635 | case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break; |
636 | default: SDL_assert(!"Unexpected audio format!" ); break; |
637 | } |
638 | |
639 | if (!filter) { |
640 | return SDL_SetError("No conversion from source format to float available" ); |
641 | } |
642 | |
643 | if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { |
644 | return -1; |
645 | } |
646 | if (src_bitsize < dst_bitsize) { |
647 | const int mult = (dst_bitsize / src_bitsize); |
648 | cvt->len_mult *= mult; |
649 | cvt->len_ratio *= mult; |
650 | } else if (src_bitsize > dst_bitsize) { |
651 | cvt->len_ratio /= (src_bitsize / dst_bitsize); |
652 | } |
653 | |
654 | retval = 1; /* added a converter. */ |
655 | } |
656 | |
657 | return retval; |
658 | } |
659 | |
660 | static int |
661 | SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt) |
662 | { |
663 | int retval = 0; /* 0 == no conversion necessary. */ |
664 | |
665 | if (!SDL_AUDIO_ISFLOAT(dst_fmt)) { |
666 | const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt); |
667 | const Uint16 src_bitsize = 32; |
668 | SDL_AudioFilter filter = NULL; |
669 | switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) { |
670 | case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break; |
671 | case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break; |
672 | case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break; |
673 | case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break; |
674 | case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break; |
675 | default: SDL_assert(!"Unexpected audio format!" ); break; |
676 | } |
677 | |
678 | if (!filter) { |
679 | return SDL_SetError("No conversion from float to format 0x%.4x available" , dst_fmt); |
680 | } |
681 | |
682 | if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { |
683 | return -1; |
684 | } |
685 | if (src_bitsize < dst_bitsize) { |
686 | const int mult = (dst_bitsize / src_bitsize); |
687 | cvt->len_mult *= mult; |
688 | cvt->len_ratio *= mult; |
689 | } else if (src_bitsize > dst_bitsize) { |
690 | cvt->len_ratio /= (src_bitsize / dst_bitsize); |
691 | } |
692 | retval = 1; /* added a converter. */ |
693 | } |
694 | |
695 | if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) { |
696 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { |
697 | return -1; |
698 | } |
699 | retval = 1; /* added a converter. */ |
700 | } |
701 | |
702 | return retval; |
703 | } |
704 | |
705 | static void |
706 | SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format) |
707 | { |
708 | /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator). |
709 | !!! FIXME in 2.1: We need to store data for this resampler, because the cvt structure doesn't store the original sample rates, |
710 | !!! FIXME in 2.1: so we steal the ninth and tenth slot. :( */ |
711 | const int inrate = (int) (size_t) cvt->filters[SDL_AUDIOCVT_MAX_FILTERS-1]; |
712 | const int outrate = (int) (size_t) cvt->filters[SDL_AUDIOCVT_MAX_FILTERS]; |
713 | const float *src = (const float *) cvt->buf; |
714 | const int srclen = cvt->len_cvt; |
715 | /*float *dst = (float *) cvt->buf; |
716 | const int dstlen = (cvt->len * cvt->len_mult);*/ |
717 | /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ |
718 | float *dst = (float *) (cvt->buf + srclen); |
719 | const int dstlen = (cvt->len * cvt->len_mult) - srclen; |
720 | const int requestedpadding = ResamplerPadding(inrate, outrate); |
721 | int paddingsamples; |
722 | float *padding; |
723 | |
724 | if (requestedpadding < SDL_MAX_SINT32 / chans) { |
725 | paddingsamples = requestedpadding * chans; |
726 | } else { |
727 | paddingsamples = 0; |
728 | } |
729 | SDL_assert(format == AUDIO_F32SYS); |
730 | |
731 | /* we keep no streaming state here, so pad with silence on both ends. */ |
732 | padding = (float *) SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof (float)); |
733 | if (!padding) { |
734 | SDL_OutOfMemory(); |
735 | return; |
736 | } |
737 | |
738 | cvt->len_cvt = SDL_ResampleAudio(chans, inrate, outrate, padding, padding, src, srclen, dst, dstlen); |
739 | |
740 | SDL_free(padding); |
741 | |
742 | SDL_memmove(cvt->buf, dst, cvt->len_cvt); /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ |
743 | |
744 | if (cvt->filters[++cvt->filter_index]) { |
745 | cvt->filters[cvt->filter_index](cvt, format); |
746 | } |
747 | } |
748 | |
749 | /* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't |
750 | !!! FIXME: store channel info, so we have to have function entry |
751 | !!! FIXME: points for each supported channel count and multiple |
752 | !!! FIXME: vs arbitrary. When we rev the ABI, clean this up. */ |
753 | #define RESAMPLER_FUNCS(chans) \ |
754 | static void SDLCALL \ |
755 | SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
756 | SDL_ResampleCVT(cvt, chans, format); \ |
757 | } |
758 | RESAMPLER_FUNCS(1) |
759 | RESAMPLER_FUNCS(2) |
760 | RESAMPLER_FUNCS(4) |
761 | RESAMPLER_FUNCS(6) |
762 | RESAMPLER_FUNCS(8) |
763 | #undef RESAMPLER_FUNCS |
764 | |
765 | static SDL_AudioFilter |
766 | ChooseCVTResampler(const int dst_channels) |
767 | { |
768 | switch (dst_channels) { |
769 | case 1: return SDL_ResampleCVT_c1; |
770 | case 2: return SDL_ResampleCVT_c2; |
771 | case 4: return SDL_ResampleCVT_c4; |
772 | case 6: return SDL_ResampleCVT_c6; |
773 | case 8: return SDL_ResampleCVT_c8; |
774 | default: break; |
775 | } |
776 | |
777 | return NULL; |
778 | } |
779 | |
780 | static int |
781 | SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels, |
782 | const int src_rate, const int dst_rate) |
783 | { |
784 | SDL_AudioFilter filter; |
785 | |
786 | if (src_rate == dst_rate) { |
787 | return 0; /* no conversion necessary. */ |
788 | } |
789 | |
790 | filter = ChooseCVTResampler(dst_channels); |
791 | if (filter == NULL) { |
792 | return SDL_SetError("No conversion available for these rates" ); |
793 | } |
794 | |
795 | if (SDL_PrepareResampleFilter() < 0) { |
796 | return -1; |
797 | } |
798 | |
799 | /* Update (cvt) with filter details... */ |
800 | if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { |
801 | return -1; |
802 | } |
803 | |
804 | /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator). |
805 | !!! FIXME in 2.1: We need to store data for this resampler, because the cvt structure doesn't store the original sample rates, |
806 | !!! FIXME in 2.1: so we steal the ninth and tenth slot. :( */ |
807 | if (cvt->filter_index >= (SDL_AUDIOCVT_MAX_FILTERS-2)) { |
808 | return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d" , SDL_AUDIOCVT_MAX_FILTERS-2); |
809 | } |
810 | cvt->filters[SDL_AUDIOCVT_MAX_FILTERS-1] = (SDL_AudioFilter) (size_t) src_rate; |
811 | cvt->filters[SDL_AUDIOCVT_MAX_FILTERS] = (SDL_AudioFilter) (size_t) dst_rate; |
812 | |
813 | if (src_rate < dst_rate) { |
814 | const double mult = ((double) dst_rate) / ((double) src_rate); |
815 | cvt->len_mult *= (int) SDL_ceil(mult); |
816 | cvt->len_ratio *= mult; |
817 | } else { |
818 | cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); |
819 | } |
820 | |
821 | /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ |
822 | /* the buffer is big enough to hold the destination now, but |
823 | we need it large enough to hold a separate scratch buffer. */ |
824 | cvt->len_mult *= 2; |
825 | |
826 | return 1; /* added a converter. */ |
827 | } |
828 | |
829 | static SDL_bool |
830 | SDL_SupportedAudioFormat(const SDL_AudioFormat fmt) |
831 | { |
832 | switch (fmt) { |
833 | case AUDIO_U8: |
834 | case AUDIO_S8: |
835 | case AUDIO_U16LSB: |
836 | case AUDIO_S16LSB: |
837 | case AUDIO_U16MSB: |
838 | case AUDIO_S16MSB: |
839 | case AUDIO_S32LSB: |
840 | case AUDIO_S32MSB: |
841 | case AUDIO_F32LSB: |
842 | case AUDIO_F32MSB: |
843 | return SDL_TRUE; /* supported. */ |
844 | |
845 | default: |
846 | break; |
847 | } |
848 | |
849 | return SDL_FALSE; /* unsupported. */ |
850 | } |
851 | |
852 | static SDL_bool |
853 | SDL_SupportedChannelCount(const int channels) |
854 | { |
855 | switch (channels) { |
856 | case 1: /* mono */ |
857 | case 2: /* stereo */ |
858 | case 4: /* quad */ |
859 | case 6: /* 5.1 */ |
860 | case 8: /* 7.1 */ |
861 | return SDL_TRUE; /* supported. */ |
862 | |
863 | default: |
864 | break; |
865 | } |
866 | |
867 | return SDL_FALSE; /* unsupported. */ |
868 | } |
869 | |
870 | |
871 | /* Creates a set of audio filters to convert from one format to another. |
872 | Returns 0 if no conversion is needed, 1 if the audio filter is set up, |
873 | or -1 if an error like invalid parameter, unsupported format, etc. occurred. |
874 | */ |
875 | |
876 | int |
877 | SDL_BuildAudioCVT(SDL_AudioCVT * cvt, |
878 | SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate, |
879 | SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate) |
880 | { |
881 | /* Sanity check target pointer */ |
882 | if (cvt == NULL) { |
883 | return SDL_InvalidParamError("cvt" ); |
884 | } |
885 | |
886 | /* Make sure we zero out the audio conversion before error checking */ |
887 | SDL_zerop(cvt); |
888 | |
889 | if (!SDL_SupportedAudioFormat(src_fmt)) { |
890 | return SDL_SetError("Invalid source format" ); |
891 | } else if (!SDL_SupportedAudioFormat(dst_fmt)) { |
892 | return SDL_SetError("Invalid destination format" ); |
893 | } else if (!SDL_SupportedChannelCount(src_channels)) { |
894 | return SDL_SetError("Invalid source channels" ); |
895 | } else if (!SDL_SupportedChannelCount(dst_channels)) { |
896 | return SDL_SetError("Invalid destination channels" ); |
897 | } else if (src_rate <= 0) { |
898 | return SDL_SetError("Source rate is equal to or less than zero" ); |
899 | } else if (dst_rate <= 0) { |
900 | return SDL_SetError("Destination rate is equal to or less than zero" ); |
901 | } else if (src_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { |
902 | return SDL_SetError("Source rate is too high" ); |
903 | } else if (dst_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { |
904 | return SDL_SetError("Destination rate is too high" ); |
905 | } |
906 | |
907 | #if DEBUG_CONVERT |
908 | printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n" , |
909 | src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate); |
910 | #endif |
911 | |
912 | /* Start off with no conversion necessary */ |
913 | cvt->src_format = src_fmt; |
914 | cvt->dst_format = dst_fmt; |
915 | cvt->needed = 0; |
916 | cvt->filter_index = 0; |
917 | SDL_zeroa(cvt->filters); |
918 | cvt->len_mult = 1; |
919 | cvt->len_ratio = 1.0; |
920 | cvt->rate_incr = ((double) dst_rate) / ((double) src_rate); |
921 | |
922 | /* Make sure we've chosen audio conversion functions (MMX, scalar, etc.) */ |
923 | SDL_ChooseAudioConverters(); |
924 | |
925 | /* Type conversion goes like this now: |
926 | - byteswap to CPU native format first if necessary. |
927 | - convert to native Float32 if necessary. |
928 | - resample and change channel count if necessary. |
929 | - convert back to native format. |
930 | - byteswap back to foreign format if necessary. |
931 | |
932 | The expectation is we can process data faster in float32 |
933 | (possibly with SIMD), and making several passes over the same |
934 | buffer is likely to be CPU cache-friendly, avoiding the |
935 | biggest performance hit in modern times. Previously we had |
936 | (script-generated) custom converters for every data type and |
937 | it was a bloat on SDL compile times and final library size. */ |
938 | |
939 | /* see if we can skip float conversion entirely. */ |
940 | if (src_rate == dst_rate && src_channels == dst_channels) { |
941 | if (src_fmt == dst_fmt) { |
942 | return 0; |
943 | } |
944 | |
945 | /* just a byteswap needed? */ |
946 | if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) { |
947 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { |
948 | return -1; |
949 | } |
950 | cvt->needed = 1; |
951 | return 1; |
952 | } |
953 | } |
954 | |
955 | /* Convert data types, if necessary. Updates (cvt). */ |
956 | if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) { |
957 | return -1; /* shouldn't happen, but just in case... */ |
958 | } |
959 | |
960 | /* Channel conversion */ |
961 | if (src_channels < dst_channels) { |
962 | /* Upmixing */ |
963 | /* Mono -> Stereo [-> ...] */ |
964 | if ((src_channels == 1) && (dst_channels > 1)) { |
965 | if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) { |
966 | return -1; |
967 | } |
968 | cvt->len_mult *= 2; |
969 | src_channels = 2; |
970 | cvt->len_ratio *= 2; |
971 | } |
972 | /* [Mono ->] Stereo -> 5.1 [-> 7.1] */ |
973 | if ((src_channels == 2) && (dst_channels >= 6)) { |
974 | if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoTo51) < 0) { |
975 | return -1; |
976 | } |
977 | src_channels = 6; |
978 | cvt->len_mult *= 3; |
979 | cvt->len_ratio *= 3; |
980 | } |
981 | /* Quad -> 5.1 [-> 7.1] */ |
982 | if ((src_channels == 4) && (dst_channels >= 6)) { |
983 | if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadTo51) < 0) { |
984 | return -1; |
985 | } |
986 | src_channels = 6; |
987 | cvt->len_mult = (cvt->len_mult * 3 + 1) / 2; |
988 | cvt->len_ratio *= 1.5; |
989 | } |
990 | /* [[Mono ->] Stereo ->] 5.1 -> 7.1 */ |
991 | if ((src_channels == 6) && (dst_channels == 8)) { |
992 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51To71) < 0) { |
993 | return -1; |
994 | } |
995 | src_channels = 8; |
996 | cvt->len_mult = (cvt->len_mult * 4 + 2) / 3; |
997 | /* Should be numerically exact with every valid input to this |
998 | function */ |
999 | cvt->len_ratio = cvt->len_ratio * 4 / 3; |
1000 | } |
1001 | /* [Mono ->] Stereo -> Quad */ |
1002 | if ((src_channels == 2) && (dst_channels == 4)) { |
1003 | if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoToQuad) < 0) { |
1004 | return -1; |
1005 | } |
1006 | src_channels = 4; |
1007 | cvt->len_mult *= 2; |
1008 | cvt->len_ratio *= 2; |
1009 | } |
1010 | } else if (src_channels > dst_channels) { |
1011 | /* Downmixing */ |
1012 | /* 7.1 -> 5.1 [-> Stereo [-> Mono]] */ |
1013 | /* 7.1 -> 5.1 [-> Quad] */ |
1014 | if ((src_channels == 8) && (dst_channels <= 6)) { |
1015 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert71To51) < 0) { |
1016 | return -1; |
1017 | } |
1018 | src_channels = 6; |
1019 | cvt->len_ratio *= 0.75; |
1020 | } |
1021 | /* [7.1 ->] 5.1 -> Stereo [-> Mono] */ |
1022 | if ((src_channels == 6) && (dst_channels <= 2)) { |
1023 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToStereo) < 0) { |
1024 | return -1; |
1025 | } |
1026 | src_channels = 2; |
1027 | cvt->len_ratio /= 3; |
1028 | } |
1029 | /* 5.1 -> Quad */ |
1030 | if ((src_channels == 6) && (dst_channels == 4)) { |
1031 | if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToQuad) < 0) { |
1032 | return -1; |
1033 | } |
1034 | src_channels = 4; |
1035 | cvt->len_ratio = cvt->len_ratio * 2 / 3; |
1036 | } |
1037 | /* Quad -> Stereo [-> Mono] */ |
1038 | if ((src_channels == 4) && (dst_channels <= 2)) { |
1039 | if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadToStereo) < 0) { |
1040 | return -1; |
1041 | } |
1042 | src_channels = 2; |
1043 | cvt->len_ratio /= 2; |
1044 | } |
1045 | /* [... ->] Stereo -> Mono */ |
1046 | if ((src_channels == 2) && (dst_channels == 1)) { |
1047 | SDL_AudioFilter filter = NULL; |
1048 | |
1049 | #if HAVE_SSE3_INTRINSICS |
1050 | if (SDL_HasSSE3()) { |
1051 | filter = SDL_ConvertStereoToMono_SSE3; |
1052 | } |
1053 | #endif |
1054 | |
1055 | if (!filter) { |
1056 | filter = SDL_ConvertStereoToMono; |
1057 | } |
1058 | |
1059 | if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { |
1060 | return -1; |
1061 | } |
1062 | |
1063 | src_channels = 1; |
1064 | cvt->len_ratio /= 2; |
1065 | } |
1066 | } |
1067 | |
1068 | if (src_channels != dst_channels) { |
1069 | /* All combinations of supported channel counts should have been |
1070 | handled by now, but let's be defensive */ |
1071 | return SDL_SetError("Invalid channel combination" ); |
1072 | } |
1073 | |
1074 | /* Do rate conversion, if necessary. Updates (cvt). */ |
1075 | if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) { |
1076 | return -1; /* shouldn't happen, but just in case... */ |
1077 | } |
1078 | |
1079 | /* Move to final data type. */ |
1080 | if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) { |
1081 | return -1; /* shouldn't happen, but just in case... */ |
1082 | } |
1083 | |
1084 | cvt->needed = (cvt->filter_index != 0); |
1085 | return (cvt->needed); |
1086 | } |
1087 | |
1088 | typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const void *inbuf, const int inbuflen, void *outbuf, const int outbuflen); |
1089 | typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream); |
1090 | typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream); |
1091 | |
1092 | struct _SDL_AudioStream |
1093 | { |
1094 | SDL_AudioCVT cvt_before_resampling; |
1095 | SDL_AudioCVT cvt_after_resampling; |
1096 | SDL_DataQueue *queue; |
1097 | SDL_bool first_run; |
1098 | Uint8 *staging_buffer; |
1099 | int staging_buffer_size; |
1100 | int staging_buffer_filled; |
1101 | Uint8 *work_buffer_base; /* maybe unaligned pointer from SDL_realloc(). */ |
1102 | int work_buffer_len; |
1103 | int src_sample_frame_size; |
1104 | SDL_AudioFormat src_format; |
1105 | Uint8 src_channels; |
1106 | int src_rate; |
1107 | int dst_sample_frame_size; |
1108 | SDL_AudioFormat dst_format; |
1109 | Uint8 dst_channels; |
1110 | int dst_rate; |
1111 | double rate_incr; |
1112 | Uint8 pre_resample_channels; |
1113 | int packetlen; |
1114 | int resampler_padding_samples; |
1115 | float *resampler_padding; |
1116 | void *resampler_state; |
1117 | SDL_ResampleAudioStreamFunc resampler_func; |
1118 | SDL_ResetAudioStreamResamplerFunc reset_resampler_func; |
1119 | SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func; |
1120 | }; |
1121 | |
1122 | static Uint8 * |
1123 | EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen) |
1124 | { |
1125 | Uint8 *ptr; |
1126 | size_t offset; |
1127 | |
1128 | if (stream->work_buffer_len >= newlen) { |
1129 | ptr = stream->work_buffer_base; |
1130 | } else { |
1131 | ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32); |
1132 | if (!ptr) { |
1133 | SDL_OutOfMemory(); |
1134 | return NULL; |
1135 | } |
1136 | /* Make sure we're aligned to 16 bytes for SIMD code. */ |
1137 | stream->work_buffer_base = ptr; |
1138 | stream->work_buffer_len = newlen; |
1139 | } |
1140 | |
1141 | offset = ((size_t) ptr) & 15; |
1142 | return offset ? ptr + (16 - offset) : ptr; |
1143 | } |
1144 | |
1145 | #ifdef HAVE_LIBSAMPLERATE_H |
1146 | static int |
1147 | SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen) |
1148 | { |
1149 | const float *inbuf = (const float *) _inbuf; |
1150 | float *outbuf = (float *) _outbuf; |
1151 | const int framelen = sizeof(float) * stream->pre_resample_channels; |
1152 | SRC_STATE *state = (SRC_STATE *)stream->resampler_state; |
1153 | SRC_DATA data; |
1154 | int result; |
1155 | |
1156 | SDL_assert(inbuf != ((const float *) outbuf)); /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */ |
1157 | |
1158 | data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */ |
1159 | data.input_frames = inbuflen / framelen; |
1160 | data.input_frames_used = 0; |
1161 | |
1162 | data.data_out = outbuf; |
1163 | data.output_frames = outbuflen / framelen; |
1164 | |
1165 | data.end_of_input = 0; |
1166 | data.src_ratio = stream->rate_incr; |
1167 | |
1168 | result = SRC_src_process(state, &data); |
1169 | if (result != 0) { |
1170 | SDL_SetError("src_process() failed: %s" , SRC_src_strerror(result)); |
1171 | return 0; |
1172 | } |
1173 | |
1174 | /* If this fails, we need to store them off somewhere */ |
1175 | SDL_assert(data.input_frames_used == data.input_frames); |
1176 | |
1177 | return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels); |
1178 | } |
1179 | |
1180 | static void |
1181 | SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream) |
1182 | { |
1183 | SRC_src_reset((SRC_STATE *)stream->resampler_state); |
1184 | } |
1185 | |
1186 | static void |
1187 | SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream) |
1188 | { |
1189 | SRC_STATE *state = (SRC_STATE *)stream->resampler_state; |
1190 | if (state) { |
1191 | SRC_src_delete(state); |
1192 | } |
1193 | |
1194 | stream->resampler_state = NULL; |
1195 | stream->resampler_func = NULL; |
1196 | stream->reset_resampler_func = NULL; |
1197 | stream->cleanup_resampler_func = NULL; |
1198 | } |
1199 | |
1200 | static SDL_bool |
1201 | SetupLibSampleRateResampling(SDL_AudioStream *stream) |
1202 | { |
1203 | int result = 0; |
1204 | SRC_STATE *state = NULL; |
1205 | |
1206 | if (SRC_available) { |
1207 | state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result); |
1208 | if (!state) { |
1209 | SDL_SetError("src_new() failed: %s" , SRC_src_strerror(result)); |
1210 | } |
1211 | } |
1212 | |
1213 | if (!state) { |
1214 | SDL_CleanupAudioStreamResampler_SRC(stream); |
1215 | return SDL_FALSE; |
1216 | } |
1217 | |
1218 | stream->resampler_state = state; |
1219 | stream->resampler_func = SDL_ResampleAudioStream_SRC; |
1220 | stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC; |
1221 | stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC; |
1222 | |
1223 | return SDL_TRUE; |
1224 | } |
1225 | #endif /* HAVE_LIBSAMPLERATE_H */ |
1226 | |
1227 | |
1228 | static int |
1229 | SDL_ResampleAudioStream(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen) |
1230 | { |
1231 | const Uint8 *inbufend = ((const Uint8 *) _inbuf) + inbuflen; |
1232 | const float *inbuf = (const float *) _inbuf; |
1233 | float *outbuf = (float *) _outbuf; |
1234 | const int chans = (int) stream->pre_resample_channels; |
1235 | const int inrate = stream->src_rate; |
1236 | const int outrate = stream->dst_rate; |
1237 | const int paddingsamples = stream->resampler_padding_samples; |
1238 | const int paddingbytes = paddingsamples * sizeof (float); |
1239 | float *lpadding = (float *) stream->resampler_state; |
1240 | const float *rpadding = (const float *) inbufend; /* we set this up so there are valid padding samples at the end of the input buffer. */ |
1241 | const int cpy = SDL_min(inbuflen, paddingbytes); |
1242 | int retval; |
1243 | |
1244 | SDL_assert(inbuf != ((const float *) outbuf)); /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */ |
1245 | |
1246 | retval = SDL_ResampleAudio(chans, inrate, outrate, lpadding, rpadding, inbuf, inbuflen, outbuf, outbuflen); |
1247 | |
1248 | /* update our left padding with end of current input, for next run. */ |
1249 | SDL_memcpy((lpadding + paddingsamples) - (cpy / sizeof (float)), inbufend - cpy, cpy); |
1250 | return retval; |
1251 | } |
1252 | |
1253 | static void |
1254 | SDL_ResetAudioStreamResampler(SDL_AudioStream *stream) |
1255 | { |
1256 | /* set all the padding to silence. */ |
1257 | const int len = stream->resampler_padding_samples; |
1258 | SDL_memset(stream->resampler_state, '\0', len * sizeof (float)); |
1259 | } |
1260 | |
1261 | static void |
1262 | SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream) |
1263 | { |
1264 | SDL_free(stream->resampler_state); |
1265 | } |
1266 | |
1267 | SDL_AudioStream * |
1268 | SDL_NewAudioStream(const SDL_AudioFormat src_format, |
1269 | const Uint8 src_channels, |
1270 | const int src_rate, |
1271 | const SDL_AudioFormat dst_format, |
1272 | const Uint8 dst_channels, |
1273 | const int dst_rate) |
1274 | { |
1275 | const int packetlen = 4096; /* !!! FIXME: good enough for now. */ |
1276 | Uint8 pre_resample_channels; |
1277 | SDL_AudioStream *retval; |
1278 | |
1279 | retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream)); |
1280 | if (!retval) { |
1281 | return NULL; |
1282 | } |
1283 | |
1284 | /* If increasing channels, do it after resampling, since we'd just |
1285 | do more work to resample duplicate channels. If we're decreasing, do |
1286 | it first so we resample the interpolated data instead of interpolating |
1287 | the resampled data (!!! FIXME: decide if that works in practice, though!). */ |
1288 | pre_resample_channels = SDL_min(src_channels, dst_channels); |
1289 | |
1290 | retval->first_run = SDL_TRUE; |
1291 | retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; |
1292 | retval->src_format = src_format; |
1293 | retval->src_channels = src_channels; |
1294 | retval->src_rate = src_rate; |
1295 | retval->dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels; |
1296 | retval->dst_format = dst_format; |
1297 | retval->dst_channels = dst_channels; |
1298 | retval->dst_rate = dst_rate; |
1299 | retval->pre_resample_channels = pre_resample_channels; |
1300 | retval->packetlen = packetlen; |
1301 | retval->rate_incr = ((double) dst_rate) / ((double) src_rate); |
1302 | retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels; |
1303 | retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof (float)); |
1304 | |
1305 | if (retval->resampler_padding == NULL) { |
1306 | SDL_FreeAudioStream(retval); |
1307 | SDL_OutOfMemory(); |
1308 | return NULL; |
1309 | } |
1310 | |
1311 | retval->staging_buffer_size = ((retval->resampler_padding_samples / retval->pre_resample_channels) * retval->src_sample_frame_size); |
1312 | if (retval->staging_buffer_size > 0) { |
1313 | retval->staging_buffer = (Uint8 *) SDL_malloc(retval->staging_buffer_size); |
1314 | if (retval->staging_buffer == NULL) { |
1315 | SDL_FreeAudioStream(retval); |
1316 | SDL_OutOfMemory(); |
1317 | return NULL; |
1318 | } |
1319 | } |
1320 | |
1321 | /* Not resampling? It's an easy conversion (and maybe not even that!) */ |
1322 | if (src_rate == dst_rate) { |
1323 | retval->cvt_before_resampling.needed = SDL_FALSE; |
1324 | if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) { |
1325 | SDL_FreeAudioStream(retval); |
1326 | return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ |
1327 | } |
1328 | } else { |
1329 | /* Don't resample at first. Just get us to Float32 format. */ |
1330 | /* !!! FIXME: convert to int32 on devices without hardware float. */ |
1331 | if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) { |
1332 | SDL_FreeAudioStream(retval); |
1333 | return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ |
1334 | } |
1335 | |
1336 | #ifdef HAVE_LIBSAMPLERATE_H |
1337 | SetupLibSampleRateResampling(retval); |
1338 | #endif |
1339 | |
1340 | if (!retval->resampler_func) { |
1341 | retval->resampler_state = SDL_calloc(retval->resampler_padding_samples, sizeof (float)); |
1342 | if (!retval->resampler_state) { |
1343 | SDL_FreeAudioStream(retval); |
1344 | SDL_OutOfMemory(); |
1345 | return NULL; |
1346 | } |
1347 | |
1348 | if (SDL_PrepareResampleFilter() < 0) { |
1349 | SDL_free(retval->resampler_state); |
1350 | retval->resampler_state = NULL; |
1351 | SDL_FreeAudioStream(retval); |
1352 | return NULL; |
1353 | } |
1354 | |
1355 | retval->resampler_func = SDL_ResampleAudioStream; |
1356 | retval->reset_resampler_func = SDL_ResetAudioStreamResampler; |
1357 | retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler; |
1358 | } |
1359 | |
1360 | /* Convert us to the final format after resampling. */ |
1361 | if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) { |
1362 | SDL_FreeAudioStream(retval); |
1363 | return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ |
1364 | } |
1365 | } |
1366 | |
1367 | retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2); |
1368 | if (!retval->queue) { |
1369 | SDL_FreeAudioStream(retval); |
1370 | return NULL; /* SDL_NewDataQueue should have called SDL_SetError. */ |
1371 | } |
1372 | |
1373 | return retval; |
1374 | } |
1375 | |
1376 | static int |
1377 | SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len, int *maxputbytes) |
1378 | { |
1379 | int buflen = len; |
1380 | int workbuflen; |
1381 | Uint8 *workbuf; |
1382 | Uint8 *resamplebuf = NULL; |
1383 | int resamplebuflen = 0; |
1384 | int neededpaddingbytes; |
1385 | int paddingbytes; |
1386 | |
1387 | /* !!! FIXME: several converters can take advantage of SIMD, but only |
1388 | !!! FIXME: if the data is aligned to 16 bytes. EnsureStreamBufferSize() |
1389 | !!! FIXME: guarantees the buffer will align, but the |
1390 | !!! FIXME: converters will iterate over the data backwards if |
1391 | !!! FIXME: the output grows, and this means we won't align if buflen |
1392 | !!! FIXME: isn't a multiple of 16. In these cases, we should chop off |
1393 | !!! FIXME: a few samples at the end and convert them separately. */ |
1394 | |
1395 | /* no padding prepended on first run. */ |
1396 | neededpaddingbytes = stream->resampler_padding_samples * sizeof (float); |
1397 | paddingbytes = stream->first_run ? 0 : neededpaddingbytes; |
1398 | stream->first_run = SDL_FALSE; |
1399 | |
1400 | /* Make sure the work buffer can hold all the data we need at once... */ |
1401 | workbuflen = buflen; |
1402 | if (stream->cvt_before_resampling.needed) { |
1403 | workbuflen *= stream->cvt_before_resampling.len_mult; |
1404 | } |
1405 | |
1406 | if (stream->dst_rate != stream->src_rate) { |
1407 | /* resamples can't happen in place, so make space for second buf. */ |
1408 | const int framesize = stream->pre_resample_channels * sizeof (float); |
1409 | const int frames = workbuflen / framesize; |
1410 | resamplebuflen = ((int) SDL_ceil(frames * stream->rate_incr)) * framesize; |
1411 | #if DEBUG_AUDIOSTREAM |
1412 | printf("AUDIOSTREAM: will resample %d bytes to %d (ratio=%.6f)\n" , workbuflen, resamplebuflen, stream->rate_incr); |
1413 | #endif |
1414 | workbuflen += resamplebuflen; |
1415 | } |
1416 | |
1417 | if (stream->cvt_after_resampling.needed) { |
1418 | /* !!! FIXME: buffer might be big enough already? */ |
1419 | workbuflen *= stream->cvt_after_resampling.len_mult; |
1420 | } |
1421 | |
1422 | workbuflen += neededpaddingbytes; |
1423 | |
1424 | #if DEBUG_AUDIOSTREAM |
1425 | printf("AUDIOSTREAM: Putting %d bytes of preconverted audio, need %d byte work buffer\n" , buflen, workbuflen); |
1426 | #endif |
1427 | |
1428 | workbuf = EnsureStreamBufferSize(stream, workbuflen); |
1429 | if (!workbuf) { |
1430 | return -1; /* probably out of memory. */ |
1431 | } |
1432 | |
1433 | resamplebuf = workbuf; /* default if not resampling. */ |
1434 | |
1435 | SDL_memcpy(workbuf + paddingbytes, buf, buflen); |
1436 | |
1437 | if (stream->cvt_before_resampling.needed) { |
1438 | stream->cvt_before_resampling.buf = workbuf + paddingbytes; |
1439 | stream->cvt_before_resampling.len = buflen; |
1440 | if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) { |
1441 | return -1; /* uhoh! */ |
1442 | } |
1443 | buflen = stream->cvt_before_resampling.len_cvt; |
1444 | |
1445 | #if DEBUG_AUDIOSTREAM |
1446 | printf("AUDIOSTREAM: After initial conversion we have %d bytes\n" , buflen); |
1447 | #endif |
1448 | } |
1449 | |
1450 | if (stream->dst_rate != stream->src_rate) { |
1451 | /* save off some samples at the end; they are used for padding now so |
1452 | the resampler is coherent and then used at the start of the next |
1453 | put operation. Prepend last put operation's padding, too. */ |
1454 | |
1455 | /* prepend prior put's padding. :P */ |
1456 | if (paddingbytes) { |
1457 | SDL_memcpy(workbuf, stream->resampler_padding, paddingbytes); |
1458 | buflen += paddingbytes; |
1459 | } |
1460 | |
1461 | /* save off the data at the end for the next run. */ |
1462 | SDL_memcpy(stream->resampler_padding, workbuf + (buflen - neededpaddingbytes), neededpaddingbytes); |
1463 | |
1464 | resamplebuf = workbuf + buflen; /* skip to second piece of workbuf. */ |
1465 | SDL_assert(buflen >= neededpaddingbytes); |
1466 | if (buflen > neededpaddingbytes) { |
1467 | buflen = stream->resampler_func(stream, workbuf, buflen - neededpaddingbytes, resamplebuf, resamplebuflen); |
1468 | } else { |
1469 | buflen = 0; |
1470 | } |
1471 | |
1472 | #if DEBUG_AUDIOSTREAM |
1473 | printf("AUDIOSTREAM: After resampling we have %d bytes\n" , buflen); |
1474 | #endif |
1475 | } |
1476 | |
1477 | if (stream->cvt_after_resampling.needed && (buflen > 0)) { |
1478 | stream->cvt_after_resampling.buf = resamplebuf; |
1479 | stream->cvt_after_resampling.len = buflen; |
1480 | if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) { |
1481 | return -1; /* uhoh! */ |
1482 | } |
1483 | buflen = stream->cvt_after_resampling.len_cvt; |
1484 | |
1485 | #if DEBUG_AUDIOSTREAM |
1486 | printf("AUDIOSTREAM: After final conversion we have %d bytes\n" , buflen); |
1487 | #endif |
1488 | } |
1489 | |
1490 | #if DEBUG_AUDIOSTREAM |
1491 | printf("AUDIOSTREAM: Final output is %d bytes\n" , buflen); |
1492 | #endif |
1493 | |
1494 | if (maxputbytes) { |
1495 | const int maxbytes = *maxputbytes; |
1496 | if (buflen > maxbytes) |
1497 | buflen = maxbytes; |
1498 | *maxputbytes -= buflen; |
1499 | } |
1500 | |
1501 | /* resamplebuf holds the final output, even if we didn't resample. */ |
1502 | return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0; |
1503 | } |
1504 | |
1505 | int |
1506 | SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len) |
1507 | { |
1508 | /* !!! FIXME: several converters can take advantage of SIMD, but only |
1509 | !!! FIXME: if the data is aligned to 16 bytes. EnsureStreamBufferSize() |
1510 | !!! FIXME: guarantees the buffer will align, but the |
1511 | !!! FIXME: converters will iterate over the data backwards if |
1512 | !!! FIXME: the output grows, and this means we won't align if buflen |
1513 | !!! FIXME: isn't a multiple of 16. In these cases, we should chop off |
1514 | !!! FIXME: a few samples at the end and convert them separately. */ |
1515 | |
1516 | #if DEBUG_AUDIOSTREAM |
1517 | printf("AUDIOSTREAM: wants to put %d preconverted bytes\n" , buflen); |
1518 | #endif |
1519 | |
1520 | if (!stream) { |
1521 | return SDL_InvalidParamError("stream" ); |
1522 | } else if (!buf) { |
1523 | return SDL_InvalidParamError("buf" ); |
1524 | } else if (len == 0) { |
1525 | return 0; /* nothing to do. */ |
1526 | } else if ((len % stream->src_sample_frame_size) != 0) { |
1527 | return SDL_SetError("Can't add partial sample frames" ); |
1528 | } |
1529 | |
1530 | if (!stream->cvt_before_resampling.needed && |
1531 | (stream->dst_rate == stream->src_rate) && |
1532 | !stream->cvt_after_resampling.needed) { |
1533 | #if DEBUG_AUDIOSTREAM |
1534 | printf("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n" , len); |
1535 | #endif |
1536 | return SDL_WriteToDataQueue(stream->queue, buf, len); |
1537 | } |
1538 | |
1539 | while (len > 0) { |
1540 | int amount; |
1541 | |
1542 | /* If we don't have a staging buffer or we're given enough data that |
1543 | we don't need to store it for later, skip the staging process. |
1544 | */ |
1545 | if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) { |
1546 | return SDL_AudioStreamPutInternal(stream, buf, len, NULL); |
1547 | } |
1548 | |
1549 | /* If there's not enough data to fill the staging buffer, just save it */ |
1550 | if ((stream->staging_buffer_filled + len) < stream->staging_buffer_size) { |
1551 | SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, len); |
1552 | stream->staging_buffer_filled += len; |
1553 | return 0; |
1554 | } |
1555 | |
1556 | /* Fill the staging buffer, process it, and continue */ |
1557 | amount = (stream->staging_buffer_size - stream->staging_buffer_filled); |
1558 | SDL_assert(amount > 0); |
1559 | SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount); |
1560 | stream->staging_buffer_filled = 0; |
1561 | if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, NULL) < 0) { |
1562 | return -1; |
1563 | } |
1564 | buf = (void *)((Uint8 *)buf + amount); |
1565 | len -= amount; |
1566 | } |
1567 | return 0; |
1568 | } |
1569 | |
1570 | int SDL_AudioStreamFlush(SDL_AudioStream *stream) |
1571 | { |
1572 | if (!stream) { |
1573 | return SDL_InvalidParamError("stream" ); |
1574 | } |
1575 | |
1576 | #if DEBUG_AUDIOSTREAM |
1577 | printf("AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes\n" , stream->staging_buffer_filled); |
1578 | #endif |
1579 | |
1580 | /* shouldn't use a staging buffer if we're not resampling. */ |
1581 | SDL_assert((stream->dst_rate != stream->src_rate) || (stream->staging_buffer_filled == 0)); |
1582 | |
1583 | if (stream->staging_buffer_filled > 0) { |
1584 | /* push the staging buffer + silence. We need to flush out not just |
1585 | the staging buffer, but the piece that the stream was saving off |
1586 | for right-side resampler padding. */ |
1587 | const SDL_bool first_run = stream->first_run; |
1588 | const int filled = stream->staging_buffer_filled; |
1589 | int actual_input_frames = filled / stream->src_sample_frame_size; |
1590 | if (!first_run) |
1591 | actual_input_frames += stream->resampler_padding_samples / stream->pre_resample_channels; |
1592 | |
1593 | if (actual_input_frames > 0) { /* don't bother if nothing to flush. */ |
1594 | /* This is how many bytes we're expecting without silence appended. */ |
1595 | int flush_remaining = ((int) SDL_ceil(actual_input_frames * stream->rate_incr)) * stream->dst_sample_frame_size; |
1596 | |
1597 | #if DEBUG_AUDIOSTREAM |
1598 | printf("AUDIOSTREAM: flushing with padding to get max %d bytes!\n" , flush_remaining); |
1599 | #endif |
1600 | |
1601 | SDL_memset(stream->staging_buffer + filled, '\0', stream->staging_buffer_size - filled); |
1602 | if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) { |
1603 | return -1; |
1604 | } |
1605 | |
1606 | /* we have flushed out (or initially filled) the pending right-side |
1607 | resampler padding, but we need to push more silence to guarantee |
1608 | the staging buffer is fully flushed out, too. */ |
1609 | SDL_memset(stream->staging_buffer, '\0', filled); |
1610 | if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) { |
1611 | return -1; |
1612 | } |
1613 | } |
1614 | } |
1615 | |
1616 | stream->staging_buffer_filled = 0; |
1617 | stream->first_run = SDL_TRUE; |
1618 | |
1619 | return 0; |
1620 | } |
1621 | |
1622 | /* get converted/resampled data from the stream */ |
1623 | int |
1624 | SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len) |
1625 | { |
1626 | #if DEBUG_AUDIOSTREAM |
1627 | printf("AUDIOSTREAM: want to get %d converted bytes\n" , len); |
1628 | #endif |
1629 | |
1630 | if (!stream) { |
1631 | return SDL_InvalidParamError("stream" ); |
1632 | } else if (!buf) { |
1633 | return SDL_InvalidParamError("buf" ); |
1634 | } else if (len <= 0) { |
1635 | return 0; /* nothing to do. */ |
1636 | } else if ((len % stream->dst_sample_frame_size) != 0) { |
1637 | return SDL_SetError("Can't request partial sample frames" ); |
1638 | } |
1639 | |
1640 | return (int) SDL_ReadFromDataQueue(stream->queue, buf, len); |
1641 | } |
1642 | |
1643 | /* number of converted/resampled bytes available */ |
1644 | int |
1645 | SDL_AudioStreamAvailable(SDL_AudioStream *stream) |
1646 | { |
1647 | return stream ? (int) SDL_CountDataQueue(stream->queue) : 0; |
1648 | } |
1649 | |
1650 | void |
1651 | SDL_AudioStreamClear(SDL_AudioStream *stream) |
1652 | { |
1653 | if (!stream) { |
1654 | SDL_InvalidParamError("stream" ); |
1655 | } else { |
1656 | SDL_ClearDataQueue(stream->queue, stream->packetlen * 2); |
1657 | if (stream->reset_resampler_func) { |
1658 | stream->reset_resampler_func(stream); |
1659 | } |
1660 | stream->first_run = SDL_TRUE; |
1661 | stream->staging_buffer_filled = 0; |
1662 | } |
1663 | } |
1664 | |
1665 | /* dispose of a stream */ |
1666 | void |
1667 | SDL_FreeAudioStream(SDL_AudioStream *stream) |
1668 | { |
1669 | if (stream) { |
1670 | if (stream->cleanup_resampler_func) { |
1671 | stream->cleanup_resampler_func(stream); |
1672 | } |
1673 | SDL_FreeDataQueue(stream->queue); |
1674 | SDL_free(stream->staging_buffer); |
1675 | SDL_free(stream->work_buffer_base); |
1676 | SDL_free(stream->resampler_padding); |
1677 | SDL_free(stream); |
1678 | } |
1679 | } |
1680 | |
1681 | /* vi: set ts=4 sw=4 expandtab: */ |
1682 | |
1683 | |