Atlas - SDL_audiocvt.c
Home / ext / SDL / src / audio Lines: 1 | Size: 54862 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#include "SDL_sysaudio.h" 24 25#include "SDL_audioqueue.h" 26#include "SDL_audioresample.h" 27 28#ifndef SDL_INT_MAX 29#define SDL_INT_MAX ((int)(~0u>>1)) 30#endif 31 32#ifdef SDL_SSE3_INTRINSICS 33// Convert from stereo to mono. Average left and right. 34static void SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(float *dst, const float *src, int num_frames) 35{ 36 LOG_DEBUG_AUDIO_CONVERT("stereo", "mono (using SSE3)"); 37 38 const __m128 divby2 = _mm_set1_ps(0.5f); 39 int i = num_frames; 40 41 /* Do SSE blocks as long as we have 16 bytes available. 42 Just use unaligned load/stores, if the memory at runtime is 43 aligned it'll be just as fast on modern processors */ 44 while (i >= 4) { // 4 * float32 45 _mm_storeu_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_loadu_ps(src), _mm_loadu_ps(src + 4)), divby2)); 46 i -= 4; 47 src += 8; 48 dst += 4; 49 } 50 51 // Finish off any leftovers with scalar operations. 52 while (i) { 53 *dst = (src[0] + src[1]) * 0.5f; 54 dst++; 55 i--; 56 src += 2; 57 } 58} 59#endif 60 61#ifdef SDL_SSE_INTRINSICS 62// Convert from mono to stereo. Duplicate to stereo left and right. 63static void SDL_TARGETING("sse") SDL_ConvertMonoToStereo_SSE(float *dst, const float *src, int num_frames) 64{ 65 LOG_DEBUG_AUDIO_CONVERT("mono", "stereo (using SSE)"); 66 67 // convert backwards, since output is growing in-place. 68 src += (num_frames-4) * 1; 69 dst += (num_frames-4) * 2; 70 71 /* Do SSE blocks as long as we have 16 bytes available. 72 Just use unaligned load/stores, if the memory at runtime is 73 aligned it'll be just as fast on modern processors */ 74 // convert backwards, since output is growing in-place. 75 int i = num_frames; 76 while (i >= 4) { // 4 * float32 77 const __m128 input = _mm_loadu_ps(src); // A B C D 78 _mm_storeu_ps(dst, _mm_unpacklo_ps(input, input)); // A A B B 79 _mm_storeu_ps(dst + 4, _mm_unpackhi_ps(input, input)); // C C D D 80 i -= 4; 81 src -= 4; 82 dst -= 8; 83 } 84 85 // Finish off any leftovers with scalar operations. 86 src += 3; 87 dst += 6; // adjust for smaller buffers. 88 while (i) { // convert backwards, since output is growing in-place. 89 const float srcFC = src[0]; 90 dst[1] /* FR */ = srcFC; 91 dst[0] /* FL */ = srcFC; 92 i--; 93 src--; 94 dst -= 2; 95 } 96} 97#endif 98 99// Include the autogenerated channel converters... 100#include "SDL_audio_channel_converters.h" 101 102static bool SDL_IsSupportedAudioFormat(const SDL_AudioFormat fmt) 103{ 104 switch (fmt) { 105 case SDL_AUDIO_U8: 106 case SDL_AUDIO_S8: 107 case SDL_AUDIO_S16LE: 108 case SDL_AUDIO_S16BE: 109 case SDL_AUDIO_S32LE: 110 case SDL_AUDIO_S32BE: 111 case SDL_AUDIO_F32LE: 112 case SDL_AUDIO_F32BE: 113 return true; // supported. 114 115 default: 116 break; 117 } 118 119 return false; // unsupported. 120} 121 122static bool SDL_IsSupportedChannelCount(const int channels) 123{ 124 return ((channels >= 1) && (channels <= 8)); 125} 126 127bool SDL_ChannelMapIsBogus(const int *chmap, int channels) 128{ 129 if (chmap) { 130 for (int i = 0; i < channels; i++) { 131 const int mapping = chmap[i]; 132 if ((mapping < -1) || (mapping >= channels)) { 133 return true; 134 } 135 } 136 } 137 return false; 138} 139 140bool SDL_ChannelMapIsDefault(const int *chmap, int channels) 141{ 142 if (chmap) { 143 for (int i = 0; i < channels; i++) { 144 if (chmap[i] != i) { 145 return false; 146 } 147 } 148 } 149 return true; 150} 151 152// Swizzle audio channels. src and dst can be the same pointer. It does not change the buffer size. 153static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const int *map, SDL_AudioFormat fmt) 154{ 155 const int bitsize = (int) SDL_AUDIO_BITSIZE(fmt); 156 157 bool has_null_mappings = false; // !!! FIXME: calculate this when setting the channel map instead. 158 for (int i = 0; i < channels; i++) { 159 if (map[i] == -1) { 160 has_null_mappings = true; 161 break; 162 } 163 } 164 165 #define CHANNEL_SWIZZLE(bits) { \ 166 Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \ 167 const Uint##bits *tsrc = (const Uint##bits *) src; \ 168 if (src != dst) { /* don't need to copy to a temporary frame first. */ \ 169 if (has_null_mappings) { \ 170 const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ 171 for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ 172 for (int ch = 0; ch < channels; ch++) { \ 173 const int m = map[ch]; \ 174 tdst[ch] = (m == -1) ? silence : tsrc[m]; \ 175 } \ 176 } \ 177 } else { \ 178 for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ 179 for (int ch = 0; ch < channels; ch++) { \ 180 tdst[ch] = tsrc[map[ch]]; \ 181 } \ 182 } \ 183 } \ 184 } else { \ 185 bool isstack; \ 186 Uint##bits *tmp = (Uint##bits *) SDL_small_alloc(int, channels, &isstack); /* !!! FIXME: allocate this when setting the channel map instead. */ \ 187 if (tmp) { \ 188 if (has_null_mappings) { \ 189 const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ 190 for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ 191 for (int ch = 0; ch < channels; ch++) { \ 192 const int m = map[ch]; \ 193 tmp[ch] = (m == -1) ? silence : tsrc[m]; \ 194 } \ 195 for (int ch = 0; ch < channels; ch++) { \ 196 tdst[ch] = tmp[ch]; \ 197 } \ 198 } \ 199 } else { \ 200 for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ 201 for (int ch = 0; ch < channels; ch++) { \ 202 tmp[ch] = tsrc[map[ch]]; \ 203 } \ 204 for (int ch = 0; ch < channels; ch++) { \ 205 tdst[ch] = tmp[ch]; \ 206 } \ 207 } \ 208 } \ 209 SDL_small_free(tmp, isstack); \ 210 } \ 211 } \ 212 } 213 214 switch (bitsize) { 215 case 8: CHANNEL_SWIZZLE(8); break; 216 case 16: CHANNEL_SWIZZLE(16); break; 217 case 32: CHANNEL_SWIZZLE(32); break; 218 // we don't currently have int64 or double audio datatypes, so no `case 64` for now. 219 default: SDL_assert(!"Unsupported audio datatype size"); break; 220 } 221 222 #undef CHANNEL_SWIZZLE 223} 224 225 226// This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream). 227// This does not check parameter validity, (beyond asserts), it expects you did that already! 228// All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience 229// if you're just going to copy the final output elsewhere, you can specify a different output pointer. 230// 231// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes. 232// If the scratch buffer is NULL, this restriction applies to the output buffer instead. 233// 234// Since this is a convenient point that audio goes through even if it doesn't need format conversion, 235// we also handle gain adjustment here, so we don't have to make another pass over the data later. 236// Strictly speaking, this is also a "conversion". :) 237void ConvertAudio(int num_frames, 238 const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map, 239 void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, 240 void *scratch, float gain) 241{ 242 SDL_assert(src != NULL); 243 SDL_assert(dst != NULL); 244 SDL_assert(SDL_IsSupportedAudioFormat(src_format)); 245 SDL_assert(SDL_IsSupportedAudioFormat(dst_format)); 246 SDL_assert(SDL_IsSupportedChannelCount(src_channels)); 247 SDL_assert(SDL_IsSupportedChannelCount(dst_channels)); 248 249 if (!num_frames) { 250 return; // no data to convert, quit. 251 } 252 253#if DEBUG_AUDIO_CONVERT 254 SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels); 255#endif 256 257 const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); 258 const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels; 259 260 const bool chmaps_match = (src_channels == dst_channels) && SDL_AudioChannelMapsEqual(src_channels, src_map, dst_map); 261 if (chmaps_match) { 262 src_map = dst_map = NULL; // NULL both these out so we don't do any unnecessary swizzling. 263 } 264 265 /* Type conversion goes like this now: 266 - swizzle through source channel map to "standard" layout. 267 - byteswap to CPU native format first if necessary. 268 - convert to native Float32 if necessary. 269 - change channel count if necessary. 270 - convert to final data format. 271 - byteswap back to foreign format if necessary. 272 - swizzle through dest channel map from "standard" layout. 273 274 The expectation is we can process data faster in float32 275 (possibly with SIMD), and making several passes over the same 276 buffer is likely to be CPU cache-friendly, avoiding the 277 biggest performance hit in modern times. Previously we had 278 (script-generated) custom converters for every data type and 279 it was a bloat on SDL compile times and final library size. */ 280 281 // swizzle input to "standard" format if necessary. 282 if (src_map) { 283 void *buf = scratch ? scratch : dst; // use scratch if available, since it has to be big enough to hold src, unless it's NULL, then dst has to be. 284 SwizzleAudio(num_frames, buf, src, src_channels, src_map, src_format); 285 src = buf; 286 } 287 288 // see if we can skip float conversion entirely. 289 if ((src_channels == dst_channels) && (gain == 1.0f)) { 290 if (src_format == dst_format) { 291 // nothing to do, we're already in the right format, just copy it over if necessary. 292 if (dst_map) { 293 SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); 294 } else if (src != dst) { 295 SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); 296 } 297 return; 298 } 299 300 // just a byteswap needed? 301 if ((src_format ^ dst_format) == SDL_AUDIO_MASK_BIG_ENDIAN) { 302 if (dst_map) { // do this first, in case we duplicate channels, we can avoid an extra copy if src != dst. 303 SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); 304 src = dst; 305 } 306 ConvertAudioSwapEndian(dst, src, num_frames * dst_channels, dst_bitsize); 307 return; // all done. 308 } 309 } 310 311 if (!scratch) { 312 scratch = dst; 313 } 314 315 const bool srcconvert = src_format != SDL_AUDIO_F32; 316 const bool channelconvert = src_channels != dst_channels; 317 const bool dstconvert = dst_format != SDL_AUDIO_F32; 318 319 // get us to float format. 320 if (srcconvert) { 321 void *buf = (channelconvert || dstconvert) ? scratch : dst; 322 ConvertAudioToFloat((float *) buf, src, num_frames * src_channels, src_format); 323 src = buf; 324 } 325 326 // Gain adjustment 327 if (gain != 1.0f) { 328 float *buf = (float *)((channelconvert || dstconvert) ? scratch : dst); 329 const int total_samples = num_frames * src_channels; 330 if (src == buf) { 331 for (int i = 0; i < total_samples; i++) { 332 buf[i] *= gain; 333 } 334 } else { 335 const float *fsrc = (const float *)src; 336 for (int i = 0; i < total_samples; i++) { 337 buf[i] = fsrc[i] * gain; 338 } 339 } 340 src = buf; 341 } 342 343 // Channel conversion 344 345 if (channelconvert) { 346 SDL_AudioChannelConverter channel_converter; 347 SDL_AudioChannelConverter override = NULL; 348 349 // SDL_IsSupportedChannelCount should have caught these asserts, or we added a new format and forgot to update the table. 350 SDL_assert(src_channels <= SDL_arraysize(channel_converters)); 351 SDL_assert(dst_channels <= SDL_arraysize(channel_converters[0])); 352 353 channel_converter = channel_converters[src_channels - 1][dst_channels - 1]; 354 SDL_assert(channel_converter != NULL); 355 356 // swap in some SIMD versions for a few of these. 357 if (channel_converter == SDL_ConvertStereoToMono) { 358 #ifdef SDL_SSE3_INTRINSICS 359 if (!override && SDL_HasSSE3()) { override = SDL_ConvertStereoToMono_SSE3; } 360 #endif 361 } else if (channel_converter == SDL_ConvertMonoToStereo) { 362 #ifdef SDL_SSE_INTRINSICS 363 if (!override && SDL_HasSSE()) { override = SDL_ConvertMonoToStereo_SSE; } 364 #endif 365 } 366 367 if (override) { 368 channel_converter = override; 369 } 370 371 void *buf = dstconvert ? scratch : dst; 372 channel_converter((float *) buf, (const float *) src, num_frames); 373 src = buf; 374 } 375 376 // Resampling is not done in here. SDL_AudioStream handles that. 377 378 // Move to final data type. 379 if (dstconvert) { 380 ConvertAudioFromFloat(dst, (const float *) src, num_frames * dst_channels, dst_format); 381 src = dst; 382 } 383 384 SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! 385 386 if (dst_map) { 387 SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); 388 } 389} 390 391// Calculate the largest frame size needed to convert between the two formats. 392static int CalculateMaxFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels) 393{ 394 const int src_format_size = SDL_AUDIO_BYTESIZE(src_format); 395 const int dst_format_size = SDL_AUDIO_BYTESIZE(dst_format); 396 const int max_app_format_size = SDL_max(src_format_size, dst_format_size); 397 const int max_format_size = SDL_max(max_app_format_size, sizeof (float)); // ConvertAudio and ResampleAudio use floats. 398 const int max_channels = SDL_max(src_channels, dst_channels); 399 return max_format_size * max_channels; 400} 401 402static Sint64 GetAudioStreamResampleRate(SDL_AudioStream *stream, int src_freq, Sint64 resample_offset) 403{ 404 src_freq = (int)((float)src_freq * stream->freq_ratio); 405 406 Sint64 resample_rate = SDL_GetResampleRate(src_freq, stream->dst_spec.freq); 407 408 // If src_freq == dst_freq, and we aren't between frames, don't resample 409 if ((resample_rate == 0x100000000) && (resample_offset == 0)) { 410 resample_rate = 0; 411 } 412 413 return resample_rate; 414} 415 416static bool UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec, const int *chmap) 417{ 418 if (SDL_AudioSpecsEqual(&stream->input_spec, spec, stream->input_chmap, chmap)) { 419 return true; 420 } 421 422 if (!SDL_ResetAudioQueueHistory(stream->queue, SDL_GetResamplerHistoryFrames())) { 423 return false; 424 } 425 426 if (!chmap) { 427 stream->input_chmap = NULL; 428 } else { 429 const size_t chmaplen = sizeof (*chmap) * spec->channels; 430 stream->input_chmap = stream->input_chmap_storage; 431 SDL_memcpy(stream->input_chmap, chmap, chmaplen); 432 } 433 434 SDL_copyp(&stream->input_spec, spec); 435 436 return true; 437} 438 439SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) 440{ 441 SDL_ChooseAudioConverters(); 442 SDL_SetupAudioResampler(); 443 444 SDL_AudioStream *result = (SDL_AudioStream *)SDL_calloc(1, sizeof(SDL_AudioStream)); 445 if (!result) { 446 return NULL; 447 } 448 449 result->freq_ratio = 1.0f; 450 result->gain = 1.0f; 451 result->queue = SDL_CreateAudioQueue(8192); 452 453 if (!result->queue) { 454 SDL_free(result); 455 return NULL; 456 } 457 458 result->lock = SDL_CreateMutex(); 459 if (!result->lock) { 460 SDL_free(result->queue); 461 SDL_free(result); 462 return NULL; 463 } 464 465 OnAudioStreamCreated(result); 466 467 if (!SDL_SetAudioStreamFormat(result, src_spec, dst_spec)) { 468 SDL_DestroyAudioStream(result); 469 return NULL; 470 } 471 472 return result; 473} 474 475SDL_PropertiesID SDL_GetAudioStreamProperties(SDL_AudioStream *stream) 476{ 477 CHECK_PARAM(!stream) { 478 SDL_InvalidParamError("stream"); 479 return 0; 480 } 481 482 SDL_LockMutex(stream->lock); 483 if (stream->props == 0) { 484 stream->props = SDL_CreateProperties(); 485 } 486 SDL_UnlockMutex(stream->lock); 487 return stream->props; 488} 489 490bool SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) 491{ 492 CHECK_PARAM(!stream) { 493 return SDL_InvalidParamError("stream"); 494 } 495 496 SDL_LockMutex(stream->lock); 497 stream->get_callback = callback; 498 stream->get_callback_userdata = userdata; 499 SDL_UnlockMutex(stream->lock); 500 return true; 501} 502 503bool SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) 504{ 505 CHECK_PARAM(!stream) { 506 return SDL_InvalidParamError("stream"); 507 } 508 509 SDL_LockMutex(stream->lock); 510 stream->put_callback = callback; 511 stream->put_callback_userdata = userdata; 512 SDL_UnlockMutex(stream->lock); 513 return true; 514} 515 516bool SDL_LockAudioStream(SDL_AudioStream *stream) 517{ 518 CHECK_PARAM(!stream) { 519 return SDL_InvalidParamError("stream"); 520 } 521 522 SDL_LockMutex(stream->lock); 523 return true; 524} 525 526bool SDL_UnlockAudioStream(SDL_AudioStream *stream) 527{ 528 CHECK_PARAM(!stream) { 529 return SDL_InvalidParamError("stream"); 530 } 531 532 SDL_UnlockMutex(stream->lock); 533 return true; 534} 535 536bool SDL_GetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioSpec *src_spec, SDL_AudioSpec *dst_spec) 537{ 538 CHECK_PARAM(!stream) { 539 if (src_spec) { 540 SDL_zerop(src_spec); 541 } 542 if (dst_spec) { 543 SDL_zerop(dst_spec); 544 } 545 return SDL_InvalidParamError("stream"); 546 } 547 548 SDL_LockMutex(stream->lock); 549 if (src_spec) { 550 SDL_copyp(src_spec, &stream->src_spec); 551 } 552 if (dst_spec) { 553 SDL_copyp(dst_spec, &stream->dst_spec); 554 } 555 SDL_UnlockMutex(stream->lock); 556 557 if (src_spec && src_spec->format == 0) { 558 return SDL_SetError("Stream has no source format"); 559 } else if (dst_spec && dst_spec->format == 0) { 560 return SDL_SetError("Stream has no destination format"); 561 } 562 563 return true; 564} 565 566bool SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) 567{ 568 CHECK_PARAM(!stream) { 569 return SDL_InvalidParamError("stream"); 570 } 571 572 // note that while we've removed the maximum frequency checks, SDL _will_ 573 // fail to resample to extremely high sample rates correctly. Really high, 574 // like 196608000Hz. File a bug. :P 575 576 if (src_spec) { 577 CHECK_PARAM(!SDL_IsSupportedAudioFormat(src_spec->format)) { 578 return SDL_InvalidParamError("src_spec->format"); 579 } 580 CHECK_PARAM(!SDL_IsSupportedChannelCount(src_spec->channels)) { 581 return SDL_InvalidParamError("src_spec->channels"); 582 } 583 CHECK_PARAM(src_spec->freq <= 0) { 584 return SDL_InvalidParamError("src_spec->freq"); 585 } 586 } 587 588 if (dst_spec) { 589 CHECK_PARAM(!SDL_IsSupportedAudioFormat(dst_spec->format)) { 590 return SDL_InvalidParamError("dst_spec->format"); 591 } 592 CHECK_PARAM(!SDL_IsSupportedChannelCount(dst_spec->channels)) { 593 return SDL_InvalidParamError("dst_spec->channels"); 594 } 595 CHECK_PARAM(dst_spec->freq <= 0) { 596 return SDL_InvalidParamError("dst_spec->freq"); 597 } 598 } 599 600 SDL_LockMutex(stream->lock); 601 602 // quietly refuse to change the format of the end currently bound to a device. 603 if (stream->bound_device) { 604 if (stream->bound_device->physical_device->recording) { 605 src_spec = NULL; 606 } else { 607 dst_spec = NULL; 608 } 609 } 610 611 if (src_spec) { 612 if (src_spec->channels != stream->src_spec.channels) { 613 SDL_free(stream->src_chmap); 614 stream->src_chmap = NULL; 615 } 616 SDL_copyp(&stream->src_spec, src_spec); 617 } 618 619 if (dst_spec) { 620 if (dst_spec->channels != stream->dst_spec.channels) { 621 SDL_free(stream->dst_chmap); 622 stream->dst_chmap = NULL; 623 } 624 SDL_copyp(&stream->dst_spec, dst_spec); 625 } 626 627 SDL_UnlockMutex(stream->lock); 628 629 return true; 630} 631 632bool SetAudioStreamChannelMap(SDL_AudioStream *stream, const SDL_AudioSpec *spec, int **stream_chmap, const int *chmap, int channels, int isinput) 633{ 634 CHECK_PARAM(!stream) { 635 return SDL_InvalidParamError("stream"); 636 } 637 638 bool result = true; 639 640 SDL_LockMutex(stream->lock); 641 642 if (channels != spec->channels) { 643 result = SDL_SetError("Wrong number of channels"); 644 } else if (!*stream_chmap && !chmap) { 645 // already at default, we're good. 646 } else if (*stream_chmap && chmap && (SDL_memcmp(*stream_chmap, chmap, sizeof (*chmap) * channels) == 0)) { 647 // already have this map, don't allocate/copy it again. 648 } else if (SDL_ChannelMapIsBogus(chmap, channels)) { 649 result = SDL_SetError("Invalid channel mapping"); 650 } else { 651 if (SDL_ChannelMapIsDefault(chmap, channels)) { 652 chmap = NULL; // just apply a default mapping. 653 } 654 if (chmap) { 655 int *dupmap = SDL_ChannelMapDup(chmap, channels); 656 if (!dupmap) { 657 result = SDL_SetError("Invalid channel mapping"); 658 } else { 659 SDL_free(*stream_chmap); 660 *stream_chmap = dupmap; 661 } 662 } else { 663 SDL_free(*stream_chmap); 664 *stream_chmap = NULL; 665 } 666 } 667 668 SDL_UnlockMutex(stream->lock); 669 return result; 670} 671 672bool SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels) 673{ 674 return SetAudioStreamChannelMap(stream, &stream->src_spec, &stream->src_chmap, chmap, channels, 1); 675} 676 677bool SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels) 678{ 679 return SetAudioStreamChannelMap(stream, &stream->dst_spec, &stream->dst_chmap, chmap, channels, 0); 680} 681 682int *SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count) 683{ 684 int *result = NULL; 685 int channels = 0; 686 if (stream) { 687 SDL_LockMutex(stream->lock); 688 channels = stream->src_spec.channels; 689 result = SDL_ChannelMapDup(stream->src_chmap, channels); 690 SDL_UnlockMutex(stream->lock); 691 } 692 693 if (count) { 694 *count = channels; 695 } 696 697 return result; 698} 699 700int *SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count) 701{ 702 int *result = NULL; 703 int channels = 0; 704 if (stream) { 705 SDL_LockMutex(stream->lock); 706 channels = stream->dst_spec.channels; 707 result = SDL_ChannelMapDup(stream->dst_chmap, channels); 708 SDL_UnlockMutex(stream->lock); 709 } 710 711 if (count) { 712 *count = channels; 713 } 714 715 return result; 716} 717 718float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream) 719{ 720 CHECK_PARAM(!stream) { 721 SDL_InvalidParamError("stream"); 722 return 0.0f; 723 } 724 725 SDL_LockMutex(stream->lock); 726 const float freq_ratio = stream->freq_ratio; 727 SDL_UnlockMutex(stream->lock); 728 729 return freq_ratio; 730} 731 732bool SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float freq_ratio) 733{ 734 CHECK_PARAM(!stream) { 735 return SDL_InvalidParamError("stream"); 736 } 737 738 // Picked mostly arbitrarily. 739 const float min_freq_ratio = 0.01f; 740 const float max_freq_ratio = 100.0f; 741 742 if (freq_ratio < min_freq_ratio) { 743 return SDL_SetError("Frequency ratio is too low"); 744 } else if (freq_ratio > max_freq_ratio) { 745 return SDL_SetError("Frequency ratio is too high"); 746 } 747 748 SDL_LockMutex(stream->lock); 749 stream->freq_ratio = freq_ratio; 750 SDL_UnlockMutex(stream->lock); 751 752 return true; 753} 754 755float SDL_GetAudioStreamGain(SDL_AudioStream *stream) 756{ 757 CHECK_PARAM(!stream) { 758 SDL_InvalidParamError("stream"); 759 return -1.0f; 760 } 761 762 SDL_LockMutex(stream->lock); 763 const float gain = stream->gain; 764 SDL_UnlockMutex(stream->lock); 765 766 return gain; 767} 768 769bool SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain) 770{ 771 CHECK_PARAM(!stream) { 772 return SDL_InvalidParamError("stream"); 773 } 774 CHECK_PARAM(gain < 0.0f) { 775 return SDL_InvalidParamError("gain"); 776 } 777 778 SDL_LockMutex(stream->lock); 779 stream->gain = gain; 780 SDL_UnlockMutex(stream->lock); 781 782 return true; 783} 784 785static bool CheckAudioStreamIsFullySetup(SDL_AudioStream *stream) 786{ 787 if (stream->src_spec.format == SDL_AUDIO_UNKNOWN) { 788 return SDL_SetError("Stream has no source format"); 789 } else if (stream->dst_spec.format == SDL_AUDIO_UNKNOWN) { 790 return SDL_SetError("Stream has no destination format"); 791 } 792 793 return true; 794} 795 796// you MUST hold `stream->lock` when calling this, and validate your parameters! 797static bool PutAudioStreamBufferInternal(SDL_AudioStream *stream, const SDL_AudioSpec *spec, const int *chmap, const void *buf, int len, SDL_ReleaseAudioBufferCallback callback, void *userdata) 798{ 799 SDL_AudioTrack *track = NULL; 800 801 if (callback) { 802 track = SDL_CreateAudioTrack(stream->queue, spec, chmap, (Uint8 *)buf, len, len, callback, userdata); 803 if (!track) { 804 return false; 805 } 806 } 807 808 const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0; 809 810 bool retval = true; 811 812 if (track) { 813 SDL_AddTrackToAudioQueue(stream->queue, track); 814 } else { 815 retval = SDL_WriteToAudioQueue(stream->queue, spec, chmap, (const Uint8 *)buf, len); 816 } 817 818 if (retval) { 819 if (stream->put_callback) { 820 const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available; 821 stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail); 822 } 823 } 824 825 return retval; 826} 827 828static bool PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int len, SDL_ReleaseAudioBufferCallback callback, void *userdata) 829{ 830#if DEBUG_AUDIOSTREAM 831 SDL_Log("AUDIOSTREAM: wants to put %d bytes", len); 832#endif 833 834 SDL_LockMutex(stream->lock); 835 836 if (!CheckAudioStreamIsFullySetup(stream)) { 837 SDL_UnlockMutex(stream->lock); 838 return false; 839 } 840 841 if ((len % SDL_AUDIO_FRAMESIZE(stream->src_spec)) != 0) { 842 SDL_UnlockMutex(stream->lock); 843 return SDL_SetError("Can't add partial sample frames"); 844 } 845 846 const bool retval = PutAudioStreamBufferInternal(stream, &stream->src_spec, stream->src_chmap, buf, len, callback, userdata); 847 848 SDL_UnlockMutex(stream->lock); 849 850 return retval; 851} 852 853static void SDLCALL FreeAllocatedAudioBuffer(void *userdata, const void *buf, int len) 854{ 855 SDL_free((void *)buf); 856} 857 858bool SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) 859{ 860 CHECK_PARAM(!stream) { 861 return SDL_InvalidParamError("stream"); 862 } 863 CHECK_PARAM(!buf) { 864 return SDL_InvalidParamError("buf"); 865 } 866 CHECK_PARAM(len < 0) { 867 return SDL_InvalidParamError("len"); 868 } 869 870 if (len == 0) { 871 return true; // nothing to do. 872 } 873 874 // When copying in large amounts of data, try and do as much work as possible 875 // outside of the stream lock, otherwise the output device is likely to be starved. 876 const int large_input_thresh = 64 * 1024; 877 878 if (len >= large_input_thresh) { 879 void *data = SDL_malloc(len); 880 881 if (!data) { 882 return false; 883 } 884 885 SDL_memcpy(data, buf, len); 886 887 bool ret = PutAudioStreamBuffer(stream, data, len, FreeAllocatedAudioBuffer, NULL); 888 if (!ret) { 889 SDL_free(data); 890 } 891 return ret; 892 } 893 894 return PutAudioStreamBuffer(stream, buf, len, NULL, NULL); 895} 896 897 898#define GENERIC_INTERLEAVE_FUNCTION(bits) \ 899 static void InterleaveAudioChannelsGeneric##bits(void *output, const void * const *channel_buffers, const int channels, int num_samples) { \ 900 Uint##bits *dst = (Uint##bits *) output; \ 901 const Uint##bits * const *srcs = (const Uint##bits * const *) channel_buffers; \ 902 for (int frame = 0; frame < num_samples; frame++) { \ 903 for (int channel = 0; channel < channels; channel++) { \ 904 *(dst++) = srcs[channel][frame]; \ 905 } \ 906 } \ 907 } 908 909GENERIC_INTERLEAVE_FUNCTION(8) 910GENERIC_INTERLEAVE_FUNCTION(16) 911GENERIC_INTERLEAVE_FUNCTION(32) 912//GENERIC_INTERLEAVE_FUNCTION(64) (we don't have any 64-bit audio data types at the moment.) 913#undef GENERIC_INTERLEAVE_FUNCTION 914 915#define GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION(bits) \ 916 static void InterleaveAudioChannelsWithNullsGeneric##bits(void *output, const void * const *channel_buffers, const int channels, int num_samples, const int isilence) { \ 917 const Uint##bits silence = (Uint##bits) isilence; \ 918 Uint##bits *dst = (Uint##bits *) output; \ 919 const Uint##bits * const *srcs = (const Uint##bits * const *) channel_buffers; \ 920 for (int frame = 0; frame < num_samples; frame++) { \ 921 for (int channel = 0; channel < channels; channel++) { \ 922 *(dst++) = srcs[channel] ? srcs[channel][frame] : silence; \ 923 } \ 924 } \ 925 } 926 927GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION(8) 928GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION(16) 929GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION(32) 930//GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION(64) (we don't have any 64-bit audio data types at the moment.) 931#undef GENERIC_INTERLEAVE_WITH_NULLS_FUNCTION 932 933static void InterleaveAudioChannels(void *output, const void * const *channel_buffers, int channels, int num_samples, const SDL_AudioSpec *spec) 934{ 935 bool have_null_channel = false; 936 void *channels_full[16]; 937 938 // if didn't specify enough channels, pad out a channel array with NULLs. 939 if ((channels >= 0) && (channels < spec->channels)) { 940 have_null_channel = true; 941 SDL_assert(SDL_IsSupportedChannelCount(spec->channels)); 942 SDL_assert(spec->channels <= SDL_arraysize(channels_full)); 943 SDL_memcpy(channels_full, channel_buffers, channels * sizeof (*channel_buffers)); 944 SDL_memset(channels_full + channels, 0, (spec->channels - channels) * sizeof (*channel_buffers)); 945 channel_buffers = (const void * const *) channels_full; 946 } 947 948 channels = spec->channels; // it's either < 0, needs to be clamped to spec->channels, or we just padded it out to spec->channels with channels_full. 949 950 if (!have_null_channel) { 951 for (int i = 0; i < channels; i++) { 952 if (channel_buffers[i] == NULL) { 953 have_null_channel = true; 954 break; 955 } 956 } 957 } 958 959 if (have_null_channel) { 960 const int silence = SDL_GetSilenceValueForFormat(spec->format); 961 switch (SDL_AUDIO_BITSIZE(spec->format)) { 962 case 8: InterleaveAudioChannelsWithNullsGeneric8(output, channel_buffers, channels, num_samples, silence); break; 963 case 16: InterleaveAudioChannelsWithNullsGeneric16(output, channel_buffers, channels, num_samples, silence); break; 964 case 32: InterleaveAudioChannelsWithNullsGeneric32(output, channel_buffers, channels, num_samples, silence); break; 965 //case 64: InterleaveAudioChannelsGeneric64(output, channel_buffers, channels, num_samples); break; (we don't have any 64-bit audio data types at the moment.) 966 default: SDL_assert(!"Missing needed generic audio interleave function!"); SDL_memset(output, 0, SDL_AUDIO_FRAMESIZE(*spec) * num_samples); break; 967 } 968 } else { 969 // !!! FIXME: it would be possible to do this really well in SIMD for stereo data, using unpack (intel) or zip (arm) instructions, etc. 970 switch (SDL_AUDIO_BITSIZE(spec->format)) { 971 case 8: InterleaveAudioChannelsGeneric8(output, channel_buffers, channels, num_samples); break; 972 case 16: InterleaveAudioChannelsGeneric16(output, channel_buffers, channels, num_samples); break; 973 case 32: InterleaveAudioChannelsGeneric32(output, channel_buffers, channels, num_samples); break; 974 //case 64: InterleaveAudioChannelsGeneric64(output, channel_buffers, channels, num_samples); break; (we don't have any 64-bit audio data types at the moment.) 975 default: SDL_assert(!"Missing needed generic audio interleave function!"); SDL_memset(output, 0, SDL_AUDIO_FRAMESIZE(*spec) * num_samples); break; 976 } 977 } 978} 979 980bool SDL_PutAudioStreamPlanarData(SDL_AudioStream *stream, const void * const *channel_buffers, int num_channels, int num_samples) 981{ 982 CHECK_PARAM(!stream) { 983 return SDL_InvalidParamError("stream"); 984 } 985 CHECK_PARAM(!channel_buffers) { 986 return SDL_InvalidParamError("channel_buffers"); 987 } 988 CHECK_PARAM(num_samples < 0) { 989 return SDL_InvalidParamError("num_samples"); 990 } 991 992 if (num_samples == 0) { 993 return true; // nothing to do. 994 } 995 996 // we do the interleaving up front without the lock held, so the audio device doesn't starve while we work. 997 // but we _do_ need to know the current input spec. 998 SDL_AudioSpec spec; 999 int chmap_copy[SDL_MAX_CHANNELMAP_CHANNELS]; 1000 int *chmap = NULL; 1001 SDL_LockMutex(stream->lock); 1002 if (!CheckAudioStreamIsFullySetup(stream)) { 1003 SDL_UnlockMutex(stream->lock); 1004 return false; 1005 } 1006 SDL_copyp(&spec, &stream->src_spec); 1007 if (stream->src_chmap) { 1008 chmap = chmap_copy; 1009 SDL_memcpy(chmap, stream->src_chmap, sizeof (*chmap) * spec.channels); 1010 } 1011 SDL_UnlockMutex(stream->lock); 1012 1013 if (spec.channels == 1) { // nothing to interleave, just use the usual function. 1014 return SDL_PutAudioStreamData(stream, channel_buffers[0], SDL_AUDIO_FRAMESIZE(spec) * num_samples); 1015 } 1016 1017 bool retval = false; 1018 1019 const int len = SDL_AUDIO_FRAMESIZE(spec) * num_samples; 1020 #if DEBUG_AUDIOSTREAM 1021 SDL_Log("AUDIOSTREAM: wants to put %d bytes of planar data", len); 1022 #endif 1023 1024 // Is the data small enough to just interleave it on the stack and put it through the normal interface? 1025 #define INTERLEAVE_STACK_SIZE 1024 1026 Uint8 stackbuf[INTERLEAVE_STACK_SIZE]; 1027 void *data = stackbuf; 1028 SDL_ReleaseAudioBufferCallback callback = NULL; 1029 1030 if (len > INTERLEAVE_STACK_SIZE) { 1031 // too big for the stack? Just SDL_malloc a block and interleave into that. To avoid the extra copy, we'll just set it as a 1032 // new track in the queue (the distinction is specifying a callback to PutAudioStreamBufferInternal, to release the buffer). 1033 data = SDL_malloc(len); 1034 if (!data) { 1035 return false; 1036 } 1037 callback = FreeAllocatedAudioBuffer; 1038 } 1039 1040 InterleaveAudioChannels(data, channel_buffers, num_channels, num_samples, &spec); 1041 1042 // it's okay if the stream format changed on another thread while we didn't hold the lock; PutAudioStreamBufferInternal will notice 1043 // and set up a new track with the right format, and the next SDL_PutAudioStreamData will notice that stream->src_spec doesn't 1044 // match the new track and set up a new one again. It's a bad idea to change the format on another thread while putting here, 1045 // but everything _will_ work out with the format that was (presumably) expected. 1046 SDL_LockMutex(stream->lock); 1047 retval = PutAudioStreamBufferInternal(stream, &spec, chmap, data, len, callback, NULL); 1048 SDL_UnlockMutex(stream->lock); 1049 1050 return retval; 1051} 1052 1053static void SDLCALL DontFreeThisAudioBuffer(void *userdata, const void *buf, int len) 1054{ 1055 // We don't own the buffer, but know it will outlive the stream 1056} 1057 1058bool SDL_PutAudioStreamDataNoCopy(SDL_AudioStream *stream, const void *buf, int len, SDL_AudioStreamDataCompleteCallback callback, void *userdata) 1059{ 1060 CHECK_PARAM(!stream) { 1061 return SDL_InvalidParamError("stream"); 1062 } 1063 CHECK_PARAM(!buf) { 1064 return SDL_InvalidParamError("buf"); 1065 } 1066 CHECK_PARAM(len < 0) { 1067 return SDL_InvalidParamError("len"); 1068 } 1069 1070 if (len == 0) { 1071 if (callback) { 1072 callback(userdata, buf, len); 1073 } 1074 return true; // nothing to do. 1075 } 1076 1077 return PutAudioStreamBuffer(stream, buf, len, callback ? callback : DontFreeThisAudioBuffer, userdata); 1078} 1079 1080bool SDL_FlushAudioStream(SDL_AudioStream *stream) 1081{ 1082 CHECK_PARAM(!stream) { 1083 return SDL_InvalidParamError("stream"); 1084 } 1085 1086 SDL_LockMutex(stream->lock); 1087 SDL_FlushAudioQueue(stream->queue); 1088 SDL_UnlockMutex(stream->lock); 1089 1090 return true; 1091} 1092 1093/* this does not save the previous contents of stream->work_buffer. It's a work buffer!! 1094 The returned buffer is aligned/padded for use with SIMD instructions. */ 1095static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) 1096{ 1097 if (stream->work_buffer_allocation >= newlen) { 1098 return stream->work_buffer; 1099 } 1100 1101 Uint8 *ptr = (Uint8 *) SDL_aligned_alloc(SDL_GetSIMDAlignment(), newlen); 1102 if (!ptr) { 1103 return NULL; // previous work buffer is still valid! 1104 } 1105 1106 SDL_aligned_free(stream->work_buffer); 1107 stream->work_buffer = ptr; 1108 stream->work_buffer_allocation = newlen; 1109 return ptr; 1110} 1111 1112static Sint64 NextAudioStreamIter(SDL_AudioStream *stream, void **inout_iter, 1113 Sint64 *inout_resample_offset, SDL_AudioSpec *out_spec, int **out_chmap, bool *out_flushed) 1114{ 1115 SDL_AudioSpec spec; 1116 bool flushed; 1117 int *chmap; 1118 size_t queued_bytes = SDL_NextAudioQueueIter(stream->queue, inout_iter, &spec, &chmap, &flushed); 1119 1120 if (out_spec) { 1121 SDL_copyp(out_spec, &spec); 1122 } 1123 1124 if (out_chmap) { 1125 *out_chmap = chmap; 1126 } 1127 1128 // There is infinite audio available, whether or not we are resampling 1129 if (queued_bytes == SDL_SIZE_MAX) { 1130 *inout_resample_offset = 0; 1131 1132 if (out_flushed) { 1133 *out_flushed = false; 1134 } 1135 1136 return SDL_MAX_SINT32; 1137 } 1138 1139 Sint64 resample_offset = *inout_resample_offset; 1140 Sint64 resample_rate = GetAudioStreamResampleRate(stream, spec.freq, resample_offset); 1141 Sint64 output_frames = (Sint64)(queued_bytes / SDL_AUDIO_FRAMESIZE(spec)); 1142 1143 if (resample_rate) { 1144 // Resampling requires padding frames to the left and right of the current position. 1145 // Past the end of the track, the right padding is filled with silence. 1146 // But we only want to do that if the track is actually finished (flushed). 1147 if (!flushed) { 1148 output_frames -= SDL_GetResamplerPaddingFrames(resample_rate); 1149 } 1150 1151 output_frames = SDL_GetResamplerOutputFrames(output_frames, resample_rate, &resample_offset); 1152 } 1153 1154 if (flushed) { 1155 resample_offset = 0; 1156 } 1157 1158 *inout_resample_offset = resample_offset; 1159 1160 if (out_flushed) { 1161 *out_flushed = flushed; 1162 } 1163 1164 return output_frames; 1165} 1166 1167static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream *stream, Sint64 *out_resample_offset) 1168{ 1169 void *iter = SDL_BeginAudioQueueIter(stream->queue); 1170 1171 Sint64 resample_offset = stream->resample_offset; 1172 Sint64 output_frames = 0; 1173 1174 while (iter) { 1175 output_frames += NextAudioStreamIter(stream, &iter, &resample_offset, NULL, NULL, NULL); 1176 1177 // Already got loads of frames. Just clamp it to something reasonable 1178 if (output_frames >= SDL_MAX_SINT32) { 1179 output_frames = SDL_MAX_SINT32; 1180 break; 1181 } 1182 } 1183 1184 if (out_resample_offset) { 1185 *out_resample_offset = resample_offset; 1186 } 1187 1188 return output_frames; 1189} 1190 1191static Sint64 GetAudioStreamHead(SDL_AudioStream *stream, SDL_AudioSpec *out_spec, int **out_chmap, bool *out_flushed) 1192{ 1193 void *iter = SDL_BeginAudioQueueIter(stream->queue); 1194 1195 if (!iter) { 1196 SDL_zerop(out_spec); 1197 *out_flushed = false; 1198 return 0; 1199 } 1200 1201 Sint64 resample_offset = stream->resample_offset; 1202 return NextAudioStreamIter(stream, &iter, &resample_offset, out_spec, out_chmap, out_flushed); 1203} 1204 1205// You must hold stream->lock and validate your parameters before calling this! 1206// Enough input data MUST be available! 1207static bool GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int output_frames, float gain) 1208{ 1209 const SDL_AudioSpec *src_spec = &stream->input_spec; 1210 const SDL_AudioSpec *dst_spec = &stream->dst_spec; 1211 1212 const SDL_AudioFormat src_format = src_spec->format; 1213 const int src_channels = src_spec->channels; 1214 1215 const SDL_AudioFormat dst_format = dst_spec->format; 1216 const int dst_channels = dst_spec->channels; 1217 const int *dst_map = stream->dst_chmap; 1218 1219 const int max_frame_size = CalculateMaxFrameSize(src_format, src_channels, dst_format, dst_channels); 1220 const Sint64 resample_rate = GetAudioStreamResampleRate(stream, src_spec->freq, stream->resample_offset); 1221 1222#if DEBUG_AUDIOSTREAM 1223 SDL_Log("AUDIOSTREAM: asking for %d frames.", output_frames); 1224#endif 1225 1226 SDL_assert(output_frames > 0); 1227 1228 // Not resampling? It's an easy conversion (and maybe not even that!) 1229 if (resample_rate == 0) { 1230 Uint8 *work_buffer = NULL; 1231 1232 // Ensure we have enough scratch space for any conversions 1233 if ((src_format != dst_format) || (src_channels != dst_channels) || (gain != 1.0f)) { 1234 work_buffer = EnsureAudioStreamWorkBufferSize(stream, output_frames * max_frame_size); 1235 1236 if (!work_buffer) { 1237 return false; 1238 } 1239 } 1240 1241 if (SDL_ReadFromAudioQueue(stream->queue, (Uint8 *)buf, dst_format, dst_channels, dst_map, 0, output_frames, 0, work_buffer, gain) != buf) { 1242 return SDL_SetError("Not enough data in queue"); 1243 } 1244 1245 return true; 1246 } 1247 1248 // Time to do some resampling! 1249 // Calculate the number of input frames necessary for this request. 1250 // Because resampling happens "between" frames, The same number of output_frames 1251 // can require a different number of input_frames, depending on the resample_offset. 1252 // In fact, input_frames can sometimes even be zero when upsampling. 1253 const int input_frames = (int) SDL_GetResamplerInputFrames(output_frames, resample_rate, stream->resample_offset); 1254 1255 const int padding_frames = SDL_GetResamplerPaddingFrames(resample_rate); 1256 1257 const SDL_AudioFormat resample_format = SDL_AUDIO_F32; 1258 1259 // If increasing channels, do it after resampling, since we'd just 1260 // do more work to resample duplicate channels. If we're decreasing, do 1261 // it first so we resample the interpolated data instead of interpolating 1262 // the resampled data. 1263 const int resample_channels = SDL_min(src_channels, dst_channels); 1264 1265 // The size of the frame used when resampling 1266 const int resample_frame_size = SDL_AUDIO_BYTESIZE(resample_format) * resample_channels; 1267 1268 // The main portion of the work_buffer can be used to store 3 things: 1269 // src_sample_frame_size * (left_padding+input_buffer+right_padding) 1270 // resample_frame_size * (left_padding+input_buffer+right_padding) 1271 // dst_sample_frame_size * output_frames 1272 // 1273 // ResampleAudio also requires an additional buffer if it can't write straight to the output: 1274 // resample_frame_size * output_frames 1275 // 1276 // Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space 1277 const int work_buffer_frames = input_frames + (padding_frames * 2); 1278 int work_buffer_capacity = work_buffer_frames * max_frame_size; 1279 int resample_buffer_offset = -1; 1280 1281 // Check if we can resample directly into the output buffer. 1282 // Note, this is just to avoid extra copies. 1283 // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer. 1284 if ((dst_format != resample_format) || (dst_channels != resample_channels)) { 1285 // Allocate space for converting the resampled output to the destination format 1286 int resample_convert_bytes = output_frames * max_frame_size; 1287 work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes); 1288 1289 // SIMD-align the buffer 1290 int simd_alignment = (int) SDL_GetSIMDAlignment(); 1291 work_buffer_capacity += simd_alignment - 1; 1292 work_buffer_capacity -= work_buffer_capacity % simd_alignment; 1293 1294 // Allocate space for the resampled output 1295 int resample_bytes = output_frames * resample_frame_size; 1296 resample_buffer_offset = work_buffer_capacity; 1297 work_buffer_capacity += resample_bytes; 1298 } 1299 1300 Uint8 *work_buffer = EnsureAudioStreamWorkBufferSize(stream, work_buffer_capacity); 1301 1302 if (!work_buffer) { 1303 return false; 1304 } 1305 1306 // adjust gain either before resampling or after, depending on which point has less 1307 // samples to process. 1308 const float preresample_gain = (input_frames > output_frames) ? 1.0f : gain; 1309 const float postresample_gain = (input_frames > output_frames) ? gain : 1.0f; 1310 1311 // (dst channel map is NULL because we'll do the final swizzle on ConvertAudio after resample.) 1312 const Uint8 *input_buffer = SDL_ReadFromAudioQueue(stream->queue, 1313 NULL, resample_format, resample_channels, NULL, 1314 padding_frames, input_frames, padding_frames, work_buffer, preresample_gain); 1315 1316 if (!input_buffer) { 1317 return SDL_SetError("Not enough data in queue (resample)"); 1318 } 1319 1320 input_buffer += padding_frames * resample_frame_size; 1321 1322 // Decide where the resampled output goes 1323 void *resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf; 1324 1325 SDL_ResampleAudio(resample_channels, 1326 (const float *)input_buffer, input_frames, 1327 (float *)resample_buffer, output_frames, 1328 resample_rate, &stream->resample_offset); 1329 1330 // Convert to the final format, if necessary (src channel map is NULL because SDL_ReadFromAudioQueue already handled this). 1331 ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, NULL, buf, dst_format, dst_channels, dst_map, work_buffer, postresample_gain); 1332 1333 return true; 1334} 1335 1336// get converted/resampled data from the stream 1337int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float extra_gain) 1338{ 1339 Uint8 *buf = (Uint8 *) voidbuf; 1340 1341#if DEBUG_AUDIOSTREAM 1342 SDL_Log("AUDIOSTREAM: want to get %d converted bytes", len); 1343#endif 1344 1345 CHECK_PARAM(!stream) { 1346 SDL_InvalidParamError("stream"); 1347 return -1; 1348 } 1349 CHECK_PARAM(!buf) { 1350 SDL_InvalidParamError("buf"); 1351 return -1; 1352 } 1353 CHECK_PARAM(len < 0) { 1354 SDL_InvalidParamError("len"); 1355 return -1; 1356 } 1357 1358 if (len == 0) { 1359 return 0; // nothing to do. 1360 } 1361 1362 SDL_LockMutex(stream->lock); 1363 1364 if (!CheckAudioStreamIsFullySetup(stream)) { 1365 SDL_UnlockMutex(stream->lock); 1366 return -1; 1367 } 1368 1369 const float gain = stream->gain * extra_gain; 1370 const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec); 1371 1372 len -= len % dst_frame_size; // chop off any fractional sample frame. 1373 1374 // give the callback a chance to fill in more stream data if it wants. 1375 if (stream->get_callback) { 1376 Sint64 total_request = len / dst_frame_size; // start with sample frames desired 1377 Sint64 additional_request = total_request; 1378 1379 Sint64 resample_offset = 0; 1380 Sint64 available_frames = GetAudioStreamAvailableFrames(stream, &resample_offset); 1381 1382 additional_request -= SDL_min(additional_request, available_frames); 1383 1384 Sint64 resample_rate = GetAudioStreamResampleRate(stream, stream->src_spec.freq, resample_offset); 1385 1386 if (resample_rate) { 1387 total_request = SDL_GetResamplerInputFrames(total_request, resample_rate, resample_offset); 1388 additional_request = SDL_GetResamplerInputFrames(additional_request, resample_rate, resample_offset); 1389 } 1390 1391 total_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. 1392 additional_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. 1393 stream->get_callback(stream->get_callback_userdata, stream, (int) SDL_min(additional_request, SDL_INT_MAX), (int) SDL_min(total_request, SDL_INT_MAX)); 1394 } 1395 1396 // Process the data in chunks to avoid allocating too much memory (and potential integer overflows) 1397 const int chunk_size = 4096; 1398 1399 int total = 0; 1400 1401 while (total < len) { 1402 // Audio is processed a track at a time. 1403 SDL_AudioSpec input_spec; 1404 int *input_chmap; 1405 bool flushed; 1406 const Sint64 available_frames = GetAudioStreamHead(stream, &input_spec, &input_chmap, &flushed); 1407 1408 if (available_frames == 0) { 1409 if (flushed) { 1410 SDL_PopAudioQueueHead(stream->queue); 1411 SDL_zero(stream->input_spec); 1412 stream->resample_offset = 0; 1413 stream->input_chmap = NULL; 1414 continue; 1415 } 1416 // There are no frames available, but the track hasn't been flushed, so more might be added later. 1417 break; 1418 } 1419 1420 if (!UpdateAudioStreamInputSpec(stream, &input_spec, input_chmap)) { 1421 total = total ? total : -1; 1422 break; 1423 } 1424 1425 // Clamp the output length to the maximum currently available. 1426 // GetAudioStreamDataInternal requires enough input data is available. 1427 int output_frames = (len - total) / dst_frame_size; 1428 output_frames = SDL_min(output_frames, chunk_size); 1429 output_frames = (int) SDL_min(output_frames, available_frames); 1430 1431 if (!GetAudioStreamDataInternal(stream, &buf[total], output_frames, gain)) { 1432 total = total ? total : -1; 1433 break; 1434 } 1435 1436 total += output_frames * dst_frame_size; 1437 } 1438 1439 SDL_UnlockMutex(stream->lock); 1440 1441#if DEBUG_AUDIOSTREAM 1442 SDL_Log("AUDIOSTREAM: Final result was %d", total); 1443#endif 1444 1445 return total; 1446} 1447 1448int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len) 1449{ 1450 return SDL_GetAudioStreamDataAdjustGain(stream, voidbuf, len, 1.0f); 1451} 1452 1453// number of converted/resampled bytes available for output 1454int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) 1455{ 1456 CHECK_PARAM(!stream) { 1457 SDL_InvalidParamError("stream"); 1458 return -1; 1459 } 1460 1461 SDL_LockMutex(stream->lock); 1462 1463 if (!CheckAudioStreamIsFullySetup(stream)) { 1464 SDL_UnlockMutex(stream->lock); 1465 return 0; 1466 } 1467 1468 Sint64 count = GetAudioStreamAvailableFrames(stream, NULL); 1469 1470 // convert from sample frames to bytes in destination format. 1471 count *= SDL_AUDIO_FRAMESIZE(stream->dst_spec); 1472 1473 SDL_UnlockMutex(stream->lock); 1474 1475 // if this overflows an int, just clamp it to a maximum. 1476 return (int) SDL_min(count, SDL_INT_MAX); 1477} 1478 1479// number of sample frames that are currently queued as input. 1480int SDL_GetAudioStreamQueued(SDL_AudioStream *stream) 1481{ 1482 CHECK_PARAM(!stream) { 1483 SDL_InvalidParamError("stream"); 1484 return -1; 1485 } 1486 1487 SDL_LockMutex(stream->lock); 1488 1489 size_t total = SDL_GetAudioQueueQueued(stream->queue); 1490 1491 SDL_UnlockMutex(stream->lock); 1492 1493 // if this overflows an int, just clamp it to a maximum. 1494 return (int) SDL_min(total, SDL_INT_MAX); 1495} 1496 1497bool SDL_ClearAudioStream(SDL_AudioStream *stream) 1498{ 1499 CHECK_PARAM(!stream) { 1500 return SDL_InvalidParamError("stream"); 1501 } 1502 1503 SDL_LockMutex(stream->lock); 1504 1505 SDL_ClearAudioQueue(stream->queue); 1506 SDL_zero(stream->input_spec); 1507 stream->input_chmap = NULL; 1508 stream->resample_offset = 0; 1509 1510 SDL_UnlockMutex(stream->lock); 1511 return true; 1512} 1513 1514void SDL_DestroyAudioStream(SDL_AudioStream *stream) 1515{ 1516 if (!stream) { 1517 return; 1518 } 1519 1520 SDL_DestroyProperties(stream->props); 1521 1522 OnAudioStreamDestroy(stream); 1523 1524 const bool simplified = stream->simplified; 1525 if (simplified) { 1526 if (stream->bound_device) { 1527 SDL_assert(stream->bound_device->simplified); 1528 SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream. 1529 } 1530 } else { 1531 SDL_UnbindAudioStream(stream); 1532 } 1533 1534 SDL_aligned_free(stream->work_buffer); 1535 SDL_DestroyAudioQueue(stream->queue); 1536 SDL_DestroyMutex(stream->lock); 1537 1538 SDL_free(stream); 1539} 1540 1541bool SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len) 1542{ 1543 if (dst_data) { 1544 *dst_data = NULL; 1545 } 1546 1547 if (dst_len) { 1548 *dst_len = 0; 1549 } 1550 1551 CHECK_PARAM(!src_data) { 1552 return SDL_InvalidParamError("src_data"); 1553 } 1554 CHECK_PARAM(src_len < 0) { 1555 return SDL_InvalidParamError("src_len"); 1556 } 1557 CHECK_PARAM(!dst_data) { 1558 return SDL_InvalidParamError("dst_data"); 1559 } 1560 CHECK_PARAM(!dst_len) { 1561 return SDL_InvalidParamError("dst_len"); 1562 } 1563 1564 bool result = false; 1565 Uint8 *dst = NULL; 1566 int dstlen = 0; 1567 1568 SDL_AudioStream *stream = SDL_CreateAudioStream(src_spec, dst_spec); 1569 if (stream) { 1570 if (SDL_PutAudioStreamDataNoCopy(stream, src_data, src_len, NULL, NULL) && SDL_FlushAudioStream(stream)) { 1571 dstlen = SDL_GetAudioStreamAvailable(stream); 1572 if (dstlen >= 0) { 1573 dst = (Uint8 *)SDL_malloc(dstlen); 1574 if (dst) { 1575 result = (SDL_GetAudioStreamData(stream, dst, dstlen) == dstlen); 1576 } 1577 } 1578 } 1579 } 1580 1581 if (result) { 1582 *dst_data = dst; 1583 *dst_len = dstlen; 1584 } else { 1585 SDL_free(dst); 1586 } 1587 1588 SDL_DestroyAudioStream(stream); 1589 return result; 1590} 1591[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.