miniaudio.h (3985334B)
1 /* 2 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. 3 miniaudio - v0.11.21 - 2023-11-15 4 5 David Reid - mackron@gmail.com 6 7 Website: https://miniaud.io 8 Documentation: https://miniaud.io/docs 9 GitHub: https://github.com/mackron/miniaudio 10 */ 11 12 /* 13 1. Introduction 14 =============== 15 miniaudio is a single file library for audio playback and capture. To use it, do the following in 16 one .c file: 17 18 ```c 19 #define MINIAUDIO_IMPLEMENTATION 20 #include "miniaudio.h" 21 ``` 22 23 You can do `#include "miniaudio.h"` in other parts of the program just like any other header. 24 25 miniaudio includes both low level and high level APIs. The low level API is good for those who want 26 to do all of their mixing themselves and only require a light weight interface to the underlying 27 audio device. The high level API is good for those who have complex mixing and effect requirements. 28 29 In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles 30 to opaque objects which means you need to allocate memory for objects yourself. In the examples 31 presented in this documentation you will often see objects declared on the stack. You need to be 32 careful when translating these examples to your own code so that you don't accidentally declare 33 your objects on the stack and then cause them to become invalid once the function returns. In 34 addition, you must ensure the memory address of your objects remain the same throughout their 35 lifetime. You therefore cannot be making copies of your objects. 36 37 A config/init pattern is used throughout the entire library. The idea is that you set up a config 38 object and pass that into the initialization routine. The advantage to this system is that the 39 config object can be initialized with logical defaults and new properties added to it without 40 breaking the API. The config object can be allocated on the stack and does not need to be 41 maintained after initialization of the corresponding object. 42 43 44 1.1. Low Level API 45 ------------------ 46 The low level API gives you access to the raw audio data of an audio device. It supports playback, 47 capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which 48 physical device(s) you want to connect to. 49 50 The low level API uses the concept of a "device" as the abstraction for physical devices. The idea 51 is that you choose a physical device to emit or capture audio from, and then move data to/from the 52 device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a 53 callback which you specify when initializing the device. 54 55 When initializing the device you first need to configure it. The device configuration allows you to 56 specify things like the format of the data delivered via the callback, the size of the internal 57 buffer and the ID of the device you want to emit or capture audio from. 58 59 Once you have the device configuration set up you can initialize the device. When initializing a 60 device you need to allocate memory for the device object beforehand. This gives the application 61 complete control over how the memory is allocated. In the example below we initialize a playback 62 device on the stack, but you could allocate it on the heap if that suits your situation better. 63 64 ```c 65 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) 66 { 67 // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both 68 // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than 69 // frameCount frames. 70 } 71 72 int main() 73 { 74 ma_device_config config = ma_device_config_init(ma_device_type_playback); 75 config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. 76 config.playback.channels = 2; // Set to 0 to use the device's native channel count. 77 config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. 78 config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. 79 config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). 80 81 ma_device device; 82 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { 83 return -1; // Failed to initialize the device. 84 } 85 86 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. 87 88 // Do something here. Probably your program's main loop. 89 90 ma_device_uninit(&device); 91 return 0; 92 } 93 ``` 94 95 In the example above, `data_callback()` is where audio data is written and read from the device. 96 The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data 97 to the output buffer (`pOutput` in the example). In capture mode you read data from the input 98 buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you 99 how many frames can be written to the output buffer and read from the input buffer. A "frame" is 100 one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 101 samples: one for the left, one for the right. The channel count is defined by the device config. 102 The size in bytes of an individual sample is defined by the sample format which is also specified 103 in the device config. Multi-channel audio data is always interleaved, which means the samples for 104 each frame are stored next to each other in memory. For example, in a stereo stream the first pair 105 of samples will be the left and right samples for the first frame, the second pair of samples will 106 be the left and right samples for the second frame, etc. 107 108 The configuration of the device is defined by the `ma_device_config` structure. The config object 109 is always initialized with `ma_device_config_init()`. It's important to always initialize the 110 config with this function as it initializes it with logical defaults and ensures your program 111 doesn't break when new members are added to the `ma_device_config` structure. The example above 112 uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes 113 a single parameter, which is whether or not the device is a playback, capture, duplex or loopback 114 device (loopback devices are not supported on all backends). The `config.playback.format` member 115 sets the sample format which can be one of the following (all formats are native-endian): 116 117 +---------------+----------------------------------------+---------------------------+ 118 | Symbol | Description | Range | 119 +---------------+----------------------------------------+---------------------------+ 120 | ma_format_f32 | 32-bit floating point | [-1, 1] | 121 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | 122 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | 123 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | 124 | ma_format_u8 | 8-bit unsigned integer | [0, 255] | 125 +---------------+----------------------------------------+---------------------------+ 126 127 The `config.playback.channels` member sets the number of channels to use with the device. The 128 channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate 129 (which must be the same for both playback and capture in full-duplex configurations). This is 130 usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between 131 8000 and 384000, however. 132 133 Note that leaving the format, channel count and/or sample rate at their default values will result 134 in the internal device's native configuration being used which is useful if you want to avoid the 135 overhead of miniaudio's automatic data conversion. 136 137 In addition to the sample format, channel count and sample rate, the data callback and user data 138 pointer are also set via the config. The user data pointer is not passed into the callback as a 139 parameter, but is instead set to the `pUserData` member of `ma_device` which you can access 140 directly since all miniaudio structures are transparent. 141 142 Initializing the device is done with `ma_device_init()`. This will return a result code telling you 143 what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is 144 complete the device will be in a stopped state. To start it, use `ma_device_start()`. 145 Uninitializing the device will stop it, which is what the example above does, but you can also stop 146 the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. 147 Note that it's important to never stop or start the device from inside the callback. This will 148 result in a deadlock. Instead you set a variable or signal an event indicating that the device 149 needs to stop and handle it in a different thread. The following APIs must never be called inside 150 the callback: 151 152 ```c 153 ma_device_init() 154 ma_device_init_ex() 155 ma_device_uninit() 156 ma_device_start() 157 ma_device_stop() 158 ``` 159 160 You must never try uninitializing and reinitializing a device inside the callback. You must also 161 never try to stop and start it from inside the callback. There are a few other things you shouldn't 162 do in the callback depending on your requirements, however this isn't so much a thread-safety 163 thing, but rather a real-time processing thing which is beyond the scope of this introduction. 164 165 The example above demonstrates the initialization of a playback device, but it works exactly the 166 same for capture. All you need to do is change the device type from `ma_device_type_playback` to 167 `ma_device_type_capture` when setting up the config, like so: 168 169 ```c 170 ma_device_config config = ma_device_config_init(ma_device_type_capture); 171 config.capture.format = MY_FORMAT; 172 config.capture.channels = MY_CHANNEL_COUNT; 173 ``` 174 175 In the data callback you just read from the input buffer (`pInput` in the example above) and leave 176 the output buffer alone (it will be set to NULL when the device type is set to 177 `ma_device_type_capture`). 178 179 These are the available device types and how you should handle the buffers in the callback: 180 181 +-------------------------+--------------------------------------------------------+ 182 | Device Type | Callback Behavior | 183 +-------------------------+--------------------------------------------------------+ 184 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | 185 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | 186 | ma_device_type_duplex | Read from input buffer, write to output buffer. | 187 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | 188 +-------------------------+--------------------------------------------------------+ 189 190 You will notice in the example above that the sample format and channel count is specified 191 separately for playback and capture. This is to support different data formats between the playback 192 and capture devices in a full-duplex system. An example may be that you want to capture audio data 193 as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you 194 use different formats between playback and capture in a full-duplex configuration you will need to 195 convert the data yourself. There are functions available to help you do this which will be 196 explained later. 197 198 The example above did not specify a physical device to connect to which means it will use the 199 operating system's default device. If you have multiple physical devices connected and you want to 200 use a specific one you will need to specify the device ID in the configuration, like so: 201 202 ```c 203 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. 204 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. 205 ``` 206 207 To retrieve the device ID you will need to perform device enumeration, however this requires the 208 use of a new concept called the "context". Conceptually speaking the context sits above the device. 209 There is one context to many devices. The purpose of the context is to represent the backend at a 210 more global level and to perform operations outside the scope of an individual device. Mainly it is 211 used for performing run-time linking against backend libraries, initializing backends and 212 enumerating devices. The example below shows how to enumerate devices. 213 214 ```c 215 ma_context context; 216 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { 217 // Error. 218 } 219 220 ma_device_info* pPlaybackInfos; 221 ma_uint32 playbackCount; 222 ma_device_info* pCaptureInfos; 223 ma_uint32 captureCount; 224 if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { 225 // Error. 226 } 227 228 // Loop over each device info and do something with it. Here we just print the name with their index. You may want 229 // to give the user the opportunity to choose which device they'd prefer. 230 for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { 231 printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); 232 } 233 234 ma_device_config config = ma_device_config_init(ma_device_type_playback); 235 config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; 236 config.playback.format = MY_FORMAT; 237 config.playback.channels = MY_CHANNEL_COUNT; 238 config.sampleRate = MY_SAMPLE_RATE; 239 config.dataCallback = data_callback; 240 config.pUserData = pMyCustomData; 241 242 ma_device device; 243 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { 244 // Error 245 } 246 247 ... 248 249 ma_device_uninit(&device); 250 ma_context_uninit(&context); 251 ``` 252 253 The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. 254 The first parameter is a pointer to a list of `ma_backend` values which are used to override the 255 default backend priorities. When this is NULL, as in this example, miniaudio's default priorities 256 are used. The second parameter is the number of backends listed in the array pointed to by the 257 first parameter. The third parameter is a pointer to a `ma_context_config` object which can be 258 NULL, in which case defaults are used. The context configuration is used for setting the logging 259 callback, custom memory allocation callbacks, user-defined data and some backend-specific 260 configurations. 261 262 Once the context has been initialized you can enumerate devices. In the example above we use the 263 simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by 264 using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer 265 to a pointer that will, upon output, be set to a pointer to a buffer containing a list of 266 `ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive 267 the number of items in the returned buffer. Do not free the returned buffers as their memory is 268 managed internally by miniaudio. 269 270 The `ma_device_info` structure contains an `id` member which is the ID you pass to the device 271 config. It also contains the name of the device which is useful for presenting a list of devices 272 to the user via the UI. 273 274 When creating your own context you will want to pass it to `ma_device_init()` when initializing the 275 device. Passing in NULL, like we do in the first example, will result in miniaudio creating the 276 context for you, which you don't want to do since you've already created a context. Note that 277 internally the context is only tracked by it's pointer which means you must not change the location 278 of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for 279 the context. 280 281 282 1.2. High Level API 283 ------------------- 284 The high level API consists of three main parts: 285 286 * Resource management for loading and streaming sounds. 287 * A node graph for advanced mixing and effect processing. 288 * A high level "engine" that wraps around the resource manager and node graph. 289 290 The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds 291 fully into memory and also streaming. It will also deal with reference counting for you which 292 avoids the same sound being loaded multiple times. 293 294 The node graph is used for mixing and effect processing. The idea is that you connect a number of 295 nodes into the graph by connecting each node's outputs to another node's inputs. Each node can 296 implement it's own effect. By chaining nodes together, advanced mixing and effect processing can 297 be achieved. 298 299 The engine encapsulates both the resource manager and the node graph to create a simple, easy to 300 use high level API. The resource manager and node graph APIs are covered in more later sections of 301 this manual. 302 303 The code below shows how you can initialize an engine using it's default configuration. 304 305 ```c 306 ma_result result; 307 ma_engine engine; 308 309 result = ma_engine_init(NULL, &engine); 310 if (result != MA_SUCCESS) { 311 return result; // Failed to initialize the engine. 312 } 313 ``` 314 315 This creates an engine instance which will initialize a device internally which you can access with 316 `ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed 317 with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which 318 means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a 319 cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. 320 321 Note that all objects in miniaudio, including the `ma_engine` object in the example above, are 322 transparent structures. There are no handles to opaque structures in miniaudio which means you need 323 to be mindful of how you declare them. In the example above we are declaring it on the stack, but 324 this will result in the struct being invalidated once the function encapsulating it returns. If 325 allocating the engine on the heap is more appropriate, you can easily do so with a standard call 326 to `malloc()` or whatever heap allocation routine you like: 327 328 ```c 329 ma_engine* pEngine = malloc(sizeof(*pEngine)); 330 ``` 331 332 The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure 333 an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of 334 `ma_engine_init()`: 335 336 ```c 337 ma_result result; 338 ma_engine engine; 339 ma_engine_config engineConfig; 340 341 engineConfig = ma_engine_config_init(); 342 engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. 343 344 result = ma_engine_init(&engineConfig, &engine); 345 if (result != MA_SUCCESS) { 346 return result; 347 } 348 ``` 349 350 This creates an engine instance using a custom config. In this particular example it's showing how 351 you can specify a custom resource manager rather than having the engine initialize one internally. 352 This is particularly useful if you want to have multiple engine's share the same resource manager. 353 354 The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. 355 356 By default the engine will be started, but nothing will be playing because no sounds have been 357 initialized. The easiest but least flexible way of playing a sound is like so: 358 359 ```c 360 ma_engine_play_sound(&engine, "my_sound.wav", NULL); 361 ``` 362 363 This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the 364 internal sound up for recycling. The last parameter is used to specify which sound group the sound 365 should be associated with which will be explained later. This particular way of playing a sound is 366 simple, but lacks flexibility and features. A more flexible way of playing a sound is to first 367 initialize a sound: 368 369 ```c 370 ma_result result; 371 ma_sound sound; 372 373 result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); 374 if (result != MA_SUCCESS) { 375 return result; 376 } 377 378 ma_sound_start(&sound); 379 ``` 380 381 This returns a `ma_sound` object which represents a single instance of the specified sound file. If 382 you want to play the same file multiple times simultaneously, you need to create one sound for each 383 instance. 384 385 Sounds should be uninitialized with `ma_sound_uninit()`. 386 387 Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with 388 `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use 389 `ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting 390 and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound 391 the be started and/or stopped at a specific time. This can be done with the following functions: 392 393 ```c 394 ma_sound_set_start_time_in_pcm_frames() 395 ma_sound_set_start_time_in_milliseconds() 396 ma_sound_set_stop_time_in_pcm_frames() 397 ma_sound_set_stop_time_in_milliseconds() 398 ``` 399 400 The start/stop time needs to be specified based on the absolute timer which is controlled by the 401 engine. The current global time time in PCM frames can be retrieved with 402 `ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with 403 `ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling 404 a start time still requires an explicit call to `ma_sound_start()` before anything will play: 405 406 ```c 407 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); 408 ma_sound_start(&sound); 409 ``` 410 411 The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be 412 loaded and a few options on which features should be enabled for that sound. By default, the sound 413 is synchronously loaded fully into memory straight from the file system without any kind of 414 decoding. If you want to decode the sound before storing it in memory, you need to specify the 415 `MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier 416 stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing 417 time which might be too expensive on the audio thread. 418 419 If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This 420 will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing 421 until the sound has had some audio decoded. 422 423 The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise 424 sounds into groups which have their own effect processing and volume control. An example is a game 425 which might have separate groups for sfx, voice and music. Each of these groups have their own 426 independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize 427 a sound group. 428 429 Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` 430 API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex 431 effect chains. 432 433 A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume 434 control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. 435 436 Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know 437 a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, 438 you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. 439 440 By default, sounds and sound groups have spatialization enabled. If you don't ever want to 441 spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The 442 spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and 443 environmental occlusion are not currently supported, but planned for the future. The supported 444 features include: 445 446 * Sound and listener positioning and orientation with cones 447 * Attenuation models: none, inverse, linear and exponential 448 * Doppler effect 449 450 Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. 451 452 To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound 453 is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with 454 `ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. 455 456 457 458 2. Building 459 =========== 460 miniaudio should work cleanly out of the box without the need to download or install any 461 dependencies. See below for platform-specific details. 462 463 Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. 464 465 If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, 466 etc. you need to link with `-latomic`. 467 468 469 2.1. Windows 470 ------------ 471 The Windows build should compile cleanly on all popular compilers without the need to configure any 472 include paths nor link to any libraries. 473 474 The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external 475 symbol for `ActivateAudioInterfaceAsync()`. 476 477 478 2.2. macOS and iOS 479 ------------------ 480 The macOS build should compile cleanly without the need to download any dependencies nor link to 481 any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to 482 link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling 483 through the command line requires linking to `-lpthread` and `-lm`. 484 485 Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's 486 notarization process. To fix this there are two options. The first is to use the 487 `MA_NO_RUNTIME_LINKING` option, like so: 488 489 ```c 490 #ifdef __APPLE__ 491 #define MA_NO_RUNTIME_LINKING 492 #endif 493 #define MINIAUDIO_IMPLEMENTATION 494 #include "miniaudio.h" 495 ``` 496 497 This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. 498 If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when 499 using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can 500 add the following to your entitlements.xcent file: 501 502 ``` 503 <key>com.apple.security.cs.allow-dyld-environment-variables</key> 504 <true/> 505 <key>com.apple.security.cs.allow-unsigned-executable-memory</key> 506 <true/> 507 ``` 508 509 See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. 510 511 512 2.3. Linux 513 ---------- 514 The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any 515 development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. 516 517 518 2.4. BSD 519 -------- 520 The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses 521 sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit 522 ARM. 523 524 525 2.5. Android 526 ------------ 527 AAudio is the highest priority backend on Android. This should work out of the box without needing 528 any kind of compiler configuration. Support for AAudio starts with Android 8 which means older 529 versions will fall back to OpenSL|ES which requires API level 16+. 530 531 There have been reports that the OpenSL|ES backend fails to initialize on some Android based 532 devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform 533 you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. 534 535 536 2.6. Emscripten 537 --------------- 538 The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. 539 You cannot use `-std=c*` compiler flags, nor `-ansi`. 540 541 You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling 542 with the following options: 543 544 -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY 545 546 An example for compiling with AudioWorklet support might look like this: 547 548 emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY 549 550 To run locally, you'll need to use emrun: 551 552 emrun bin/program.html 553 554 555 556 2.7. Build Options 557 ------------------ 558 `#define` these options before including miniaudio.h. 559 560 +----------------------------------+--------------------------------------------------------------------+ 561 | Option | Description | 562 +----------------------------------+--------------------------------------------------------------------+ 563 | MA_NO_WASAPI | Disables the WASAPI backend. | 564 +----------------------------------+--------------------------------------------------------------------+ 565 | MA_NO_DSOUND | Disables the DirectSound backend. | 566 +----------------------------------+--------------------------------------------------------------------+ 567 | MA_NO_WINMM | Disables the WinMM backend. | 568 +----------------------------------+--------------------------------------------------------------------+ 569 | MA_NO_ALSA | Disables the ALSA backend. | 570 +----------------------------------+--------------------------------------------------------------------+ 571 | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | 572 +----------------------------------+--------------------------------------------------------------------+ 573 | MA_NO_JACK | Disables the JACK backend. | 574 +----------------------------------+--------------------------------------------------------------------+ 575 | MA_NO_COREAUDIO | Disables the Core Audio backend. | 576 +----------------------------------+--------------------------------------------------------------------+ 577 | MA_NO_SNDIO | Disables the sndio backend. | 578 +----------------------------------+--------------------------------------------------------------------+ 579 | MA_NO_AUDIO4 | Disables the audio(4) backend. | 580 +----------------------------------+--------------------------------------------------------------------+ 581 | MA_NO_OSS | Disables the OSS backend. | 582 +----------------------------------+--------------------------------------------------------------------+ 583 | MA_NO_AAUDIO | Disables the AAudio backend. | 584 +----------------------------------+--------------------------------------------------------------------+ 585 | MA_NO_OPENSL | Disables the OpenSL|ES backend. | 586 +----------------------------------+--------------------------------------------------------------------+ 587 | MA_NO_WEBAUDIO | Disables the Web Audio backend. | 588 +----------------------------------+--------------------------------------------------------------------+ 589 | MA_NO_NULL | Disables the null backend. | 590 +----------------------------------+--------------------------------------------------------------------+ 591 | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | 592 | | enable specific backends. | 593 +----------------------------------+--------------------------------------------------------------------+ 594 | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 595 | | enable the WASAPI backend. | 596 +----------------------------------+--------------------------------------------------------------------+ 597 | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 598 | | enable the DirectSound backend. | 599 +----------------------------------+--------------------------------------------------------------------+ 600 | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 601 | | enable the WinMM backend. | 602 +----------------------------------+--------------------------------------------------------------------+ 603 | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 604 | | enable the ALSA backend. | 605 +----------------------------------+--------------------------------------------------------------------+ 606 | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 607 | | enable the PulseAudio backend. | 608 +----------------------------------+--------------------------------------------------------------------+ 609 | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 610 | | enable the JACK backend. | 611 +----------------------------------+--------------------------------------------------------------------+ 612 | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 613 | | enable the Core Audio backend. | 614 +----------------------------------+--------------------------------------------------------------------+ 615 | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 616 | | enable the sndio backend. | 617 +----------------------------------+--------------------------------------------------------------------+ 618 | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 619 | | enable the audio(4) backend. | 620 +----------------------------------+--------------------------------------------------------------------+ 621 | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 622 | | enable the OSS backend. | 623 +----------------------------------+--------------------------------------------------------------------+ 624 | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 625 | | enable the AAudio backend. | 626 +----------------------------------+--------------------------------------------------------------------+ 627 | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 628 | | enable the OpenSL|ES backend. | 629 +----------------------------------+--------------------------------------------------------------------+ 630 | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 631 | | enable the Web Audio backend. | 632 +----------------------------------+--------------------------------------------------------------------+ 633 | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | 634 | | enable the null backend. | 635 +----------------------------------+--------------------------------------------------------------------+ 636 | MA_NO_DECODING | Disables decoding APIs. | 637 +----------------------------------+--------------------------------------------------------------------+ 638 | MA_NO_ENCODING | Disables encoding APIs. | 639 +----------------------------------+--------------------------------------------------------------------+ 640 | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | 641 +----------------------------------+--------------------------------------------------------------------+ 642 | MA_NO_FLAC | Disables the built-in FLAC decoder. | 643 +----------------------------------+--------------------------------------------------------------------+ 644 | MA_NO_MP3 | Disables the built-in MP3 decoder. | 645 +----------------------------------+--------------------------------------------------------------------+ 646 | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | 647 | | and `ma_device` APIs. This is useful if you only want to use | 648 | | miniaudio's data conversion and/or decoding APIs. | 649 +----------------------------------+--------------------------------------------------------------------+ 650 | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | 651 | | also disable the following functions: | 652 | | | 653 | | ``` | 654 | | ma_sound_init_from_file() | 655 | | ma_sound_init_from_file_w() | 656 | | ma_sound_init_copy() | 657 | | ma_engine_play_sound_ex() | 658 | | ma_engine_play_sound() | 659 | | ``` | 660 | | | 661 | | The only way to initialize a `ma_sound` object is to initialize it | 662 | | from a data source. | 663 +----------------------------------+--------------------------------------------------------------------+ 664 | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | 665 | | because it depends on the node graph. | 666 +----------------------------------+--------------------------------------------------------------------+ 667 | MA_NO_ENGINE | Disables the engine API. | 668 +----------------------------------+--------------------------------------------------------------------+ 669 | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | 670 | | `ma_event` APIs. This option is useful if you only need to use | 671 | | miniaudio for data conversion, decoding and/or encoding. Some | 672 | | families of APIs require threading which means the following | 673 | | options must also be set: | 674 | | | 675 | | ``` | 676 | | MA_NO_DEVICE_IO | 677 | | ``` | 678 +----------------------------------+--------------------------------------------------------------------+ 679 | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | 680 +----------------------------------+--------------------------------------------------------------------+ 681 | MA_NO_SSE2 | Disables SSE2 optimizations. | 682 +----------------------------------+--------------------------------------------------------------------+ 683 | MA_NO_AVX2 | Disables AVX2 optimizations. | 684 +----------------------------------+--------------------------------------------------------------------+ 685 | MA_NO_NEON | Disables NEON optimizations. | 686 +----------------------------------+--------------------------------------------------------------------+ 687 | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | 688 | | notarization process. When enabling this, you may need to avoid | 689 | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | 690 | | up with compilation errors due to conflicts with `timespec` and | 691 | | `timeval` data types. | 692 | | | 693 | | You may need to enable this if your target platform does not allow | 694 | | runtime linking via `dlopen()`. | 695 +----------------------------------+--------------------------------------------------------------------+ 696 | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | 697 +----------------------------------+--------------------------------------------------------------------+ 698 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | 699 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | 700 +----------------------------------+--------------------------------------------------------------------+ 701 | MA_API | Controls how public APIs should be decorated. Default is `extern`. | 702 +----------------------------------+--------------------------------------------------------------------+ 703 704 705 3. Definitions 706 ============== 707 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity 708 in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio 709 uses each term. 710 711 3.1. Sample 712 ----------- 713 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit 714 floating point number. 715 716 3.2. Frame / PCM Frame 717 ---------------------- 718 A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 719 samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" 720 and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. 721 If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always 722 clarify what it's referring to with something like "FLAC frame". 723 724 3.3. Channel 725 ------------ 726 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or 727 received from an individual microphone in a microphone system. A stereo stream has two channels (a 728 left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio 729 systems refer to a channel as a complex audio stream that's mixed with other channels to produce 730 the final mix - this is completely different to miniaudio's use of the term "channel" and should 731 not be confused. 732 733 3.4. Sample Rate 734 ---------------- 735 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number 736 of PCM frames that are processed per second. 737 738 3.5. Formats 739 ------------ 740 Throughout miniaudio you will see references to different sample formats: 741 742 +---------------+----------------------------------------+---------------------------+ 743 | Symbol | Description | Range | 744 +---------------+----------------------------------------+---------------------------+ 745 | ma_format_f32 | 32-bit floating point | [-1, 1] | 746 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | 747 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | 748 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | 749 | ma_format_u8 | 8-bit unsigned integer | [0, 255] | 750 +---------------+----------------------------------------+---------------------------+ 751 752 All formats are native-endian. 753 754 755 756 4. Data Sources 757 =============== 758 The data source abstraction in miniaudio is used for retrieving audio data from some source. A few 759 examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data 760 sources in order to make sense of some of the higher level concepts in miniaudio. 761 762 The `ma_data_source` API is a generic interface for reading from a data source. Any object that 763 implements the data source interface can be plugged into any `ma_data_source` function. 764 765 To read data from a data source: 766 767 ```c 768 ma_result result; 769 ma_uint64 framesRead; 770 771 result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); 772 if (result != MA_SUCCESS) { 773 return result; // Failed to read data from the data source. 774 } 775 ``` 776 777 If you don't need the number of frames that were successfully read you can pass in `NULL` to the 778 `pFramesRead` parameter. If this returns a value less than the number of frames requested it means 779 the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames 780 read is 0. 781 782 When calling any data source function, with the exception of `ma_data_source_init()` and 783 `ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, 784 you could plug in a decoder like so: 785 786 ```c 787 ma_result result; 788 ma_uint64 framesRead; 789 ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. 790 791 result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); 792 if (result != MA_SUCCESS) { 793 return result; // Failed to read data from the decoder. 794 } 795 ``` 796 797 If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you 798 can use `ma_data_source_seek_pcm_frames()`. 799 800 To seek to a specific PCM frame: 801 802 ```c 803 result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); 804 if (result != MA_SUCCESS) { 805 return result; // Failed to seek to PCM frame. 806 } 807 ``` 808 809 You can retrieve the total length of a data source in PCM frames, but note that some data sources 810 may not have the notion of a length, such as noise and waveforms, and others may just not have a 811 way of determining the length such as some decoders. To retrieve the length: 812 813 ```c 814 ma_uint64 length; 815 816 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); 817 if (result != MA_SUCCESS) { 818 return result; // Failed to retrieve the length. 819 } 820 ``` 821 822 Care should be taken when retrieving the length of a data source where the underlying decoder is 823 pulling data from a data stream with an undefined length, such as internet radio or some kind of 824 broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. 825 826 The current position of the cursor in PCM frames can also be retrieved: 827 828 ```c 829 ma_uint64 cursor; 830 831 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); 832 if (result != MA_SUCCESS) { 833 return result; // Failed to retrieve the cursor. 834 } 835 ``` 836 837 You will often need to know the data format that will be returned after reading. This can be 838 retrieved like so: 839 840 ```c 841 ma_format format; 842 ma_uint32 channels; 843 ma_uint32 sampleRate; 844 ma_channel channelMap[MA_MAX_CHANNELS]; 845 846 result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); 847 if (result != MA_SUCCESS) { 848 return result; // Failed to retrieve data format. 849 } 850 ``` 851 852 If you do not need a specific data format property, just pass in NULL to the respective parameter. 853 854 There may be cases where you want to implement something like a sound bank where you only want to 855 read data within a certain range of the underlying data. To do this you can use a range: 856 857 ```c 858 result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); 859 if (result != MA_SUCCESS) { 860 return result; // Failed to set the range. 861 } 862 ``` 863 864 This is useful if you have a sound bank where many sounds are stored in the same file and you want 865 the data source to only play one of those sub-sounds. Note that once the range is set, everything 866 that takes a position, such as cursors and loop points, should always be relatvie to the start of 867 the range. When the range is set, any previously defined loop point will be reset. 868 869 Custom loop points can also be used with data sources. By default, data sources will loop after 870 they reach the end of the data source, but if you need to loop at a specific location, you can do 871 the following: 872 873 ```c 874 result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); 875 if (result != MA_SUCCESS) { 876 return result; // Failed to set the loop point. 877 } 878 ``` 879 880 The loop point is relative to the current range. 881 882 It's sometimes useful to chain data sources together so that a seamless transition can be achieved. 883 To do this, you can use chaining: 884 885 ```c 886 ma_decoder decoder1; 887 ma_decoder decoder2; 888 889 // ... initialize decoders with ma_decoder_init_*() ... 890 891 result = ma_data_source_set_next(&decoder1, &decoder2); 892 if (result != MA_SUCCESS) { 893 return result; // Failed to set the next data source. 894 } 895 896 result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); 897 if (result != MA_SUCCESS) { 898 return result; // Failed to read from the decoder. 899 } 900 ``` 901 902 In the example above we're using decoders. When reading from a chain, you always want to read from 903 the top level data source in the chain. In the example above, `decoder1` is the top level data 904 source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any 905 gaps. 906 907 Note that when looping is enabled, only the current data source will be looped. You can loop the 908 entire chain by linking in a loop like so: 909 910 ```c 911 ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 912 ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). 913 ``` 914 915 Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically 916 changing links while the audio thread is in the middle of reading. 917 918 Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple 919 instances of the same sound simultaneously. This can be extremely inefficient depending on the type 920 of data source and can result in glitching due to subtle changes to the state of internal filters. 921 Instead, initialize multiple data sources for each instance. 922 923 924 4.1. Custom Data Sources 925 ------------------------ 926 You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. 927 Your custom object must have `ma_data_source_base` as it's first member: 928 929 ```c 930 struct my_data_source 931 { 932 ma_data_source_base base; 933 ... 934 }; 935 ``` 936 937 In your initialization routine, you need to call `ma_data_source_init()` in order to set up the 938 base object (`ma_data_source_base`): 939 940 ```c 941 static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 942 { 943 // Read data here. Output in the same format returned by my_data_source_get_data_format(). 944 } 945 946 static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 947 { 948 // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. 949 } 950 951 static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 952 { 953 // Return the format of the data here. 954 } 955 956 static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 957 { 958 // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. 959 } 960 961 static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 962 { 963 // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. 964 } 965 966 static ma_data_source_vtable g_my_data_source_vtable = 967 { 968 my_data_source_read, 969 my_data_source_seek, 970 my_data_source_get_data_format, 971 my_data_source_get_cursor, 972 my_data_source_get_length 973 }; 974 975 ma_result my_data_source_init(my_data_source* pMyDataSource) 976 { 977 ma_result result; 978 ma_data_source_config baseConfig; 979 980 baseConfig = ma_data_source_config_init(); 981 baseConfig.vtable = &g_my_data_source_vtable; 982 983 result = ma_data_source_init(&baseConfig, &pMyDataSource->base); 984 if (result != MA_SUCCESS) { 985 return result; 986 } 987 988 // ... do the initialization of your custom data source here ... 989 990 return MA_SUCCESS; 991 } 992 993 void my_data_source_uninit(my_data_source* pMyDataSource) 994 { 995 // ... do the uninitialization of your custom data source here ... 996 997 // You must uninitialize the base data source. 998 ma_data_source_uninit(&pMyDataSource->base); 999 } 1000 ``` 1001 1002 Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside 1003 of the custom data source. It's up to the custom data source itself to call these within their own 1004 init/uninit functions. 1005 1006 1007 1008 5. Engine 1009 ========= 1010 The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The 1011 `ma_engine` object encapsulates a resource manager and a node graph, both of which will be 1012 explained in more detail later. 1013 1014 Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing 1015 group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and 1016 `ma_sound_group` objects are nodes within the engine's node graph. 1017 1018 When the engine is initialized, it will normally create a device internally. If you would rather 1019 manage the device yourself, you can do so and just pass a pointer to it via the engine config when 1020 you initialize the engine. You can also just use the engine without a device, which again can be 1021 configured via the engine config. 1022 1023 The most basic way to initialize the engine is with a default config, like so: 1024 1025 ```c 1026 ma_result result; 1027 ma_engine engine; 1028 1029 result = ma_engine_init(NULL, &engine); 1030 if (result != MA_SUCCESS) { 1031 return result; // Failed to initialize the engine. 1032 } 1033 ``` 1034 1035 This will result in the engine initializing a playback device using the operating system's default 1036 device. This will be sufficient for many use cases, but if you need more flexibility you'll want to 1037 configure the engine with an engine config: 1038 1039 ```c 1040 ma_result result; 1041 ma_engine engine; 1042 ma_engine_config engineConfig; 1043 1044 engineConfig = ma_engine_config_init(); 1045 engineConfig.pDevice = &myDevice; 1046 1047 result = ma_engine_init(&engineConfig, &engine); 1048 if (result != MA_SUCCESS) { 1049 return result; // Failed to initialize the engine. 1050 } 1051 ``` 1052 1053 In the example above we're passing in a pre-initialized device. Since the caller is the one in 1054 control of the device's data callback, it's their responsibility to manually call 1055 `ma_engine_read_pcm_frames()` from inside their data callback: 1056 1057 ```c 1058 void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) 1059 { 1060 ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); 1061 } 1062 ``` 1063 1064 You can also use the engine independent of a device entirely: 1065 1066 ```c 1067 ma_result result; 1068 ma_engine engine; 1069 ma_engine_config engineConfig; 1070 1071 engineConfig = ma_engine_config_init(); 1072 engineConfig.noDevice = MA_TRUE; 1073 engineConfig.channels = 2; // Must be set when not using a device. 1074 engineConfig.sampleRate = 48000; // Must be set when not using a device. 1075 1076 result = ma_engine_init(&engineConfig, &engine); 1077 if (result != MA_SUCCESS) { 1078 return result; // Failed to initialize the engine. 1079 } 1080 ``` 1081 1082 Note that when you're not using a device, you must set the channel count and sample rate in the 1083 config or else miniaudio won't know what to use (miniaudio will use the device to determine this 1084 normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio 1085 data from the engine. This kind of setup is useful if you want to do something like offline 1086 processing or want to use a different audio system for playback such as SDL. 1087 1088 When a sound is loaded it goes through a resource manager. By default the engine will initialize a 1089 resource manager internally, but you can also specify a pre-initialized resource manager: 1090 1091 ```c 1092 ma_result result; 1093 ma_engine engine1; 1094 ma_engine engine2; 1095 ma_engine_config engineConfig; 1096 1097 engineConfig = ma_engine_config_init(); 1098 engineConfig.pResourceManager = &myResourceManager; 1099 1100 ma_engine_init(&engineConfig, &engine1); 1101 ma_engine_init(&engineConfig, &engine2); 1102 ``` 1103 1104 In this example we are initializing two engines, both of which are sharing the same resource 1105 manager. This is especially useful for saving memory when loading the same file across multiple 1106 engines. If you were not to use a shared resource manager, each engine instance would use their own 1107 which would result in any sounds that are used between both engine's being loaded twice. By using 1108 a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you 1109 need to output to multiple playback devices, such as in a local multiplayer game where each player 1110 is using their own set of headphones. 1111 1112 By default an engine will be in a started state. To make it so the engine is not automatically 1113 started you can configure it as such: 1114 1115 ```c 1116 engineConfig.noAutoStart = MA_TRUE; 1117 1118 // The engine will need to be started manually. 1119 ma_engine_start(&engine); 1120 1121 // Later on the engine can be stopped with ma_engine_stop(). 1122 ma_engine_stop(&engine); 1123 ``` 1124 1125 The concept of starting or stopping an engine is only relevant when using the engine with a 1126 device. Attempting to start or stop an engine that is not associated with a device will result in 1127 `MA_INVALID_OPERATION`. 1128 1129 The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a 1130 linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you 1131 prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. 1132 1133 When a sound is spatialized, it is done so relative to a listener. An engine can be configured to 1134 have multiple listeners which can be configured via the config: 1135 1136 ```c 1137 engineConfig.listenerCount = 2; 1138 ``` 1139 1140 The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a 1141 sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound 1142 to a specific listener which will be explained later. Listener's have a position, direction, cone, 1143 and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up 1144 to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The 1145 position, direction and velocity are all specified in absolute terms: 1146 1147 ```c 1148 ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); 1149 ``` 1150 1151 The direction of the listener represents it's forward vector. The listener's up vector can also be 1152 specified and defaults to +1 on the Y axis. 1153 1154 ```c 1155 ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); 1156 ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); 1157 ``` 1158 1159 The engine supports directional attenuation. The listener can have a cone the controls how sound is 1160 attenuated based on the listener's direction. When a sound is between the inner and outer cones, it 1161 will be attenuated between 1 and the cone's outer gain: 1162 1163 ```c 1164 ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); 1165 ``` 1166 1167 When a sound is inside the inner code, no directional attenuation is applied. When the sound is 1168 outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When 1169 the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 1170 and the outer gain. 1171 1172 The engine's coordinate system follows the OpenGL coordinate system where positive X points right, 1173 positive Y points up and negative Z points forward. 1174 1175 The simplest and least flexible way to play a sound is like so: 1176 1177 ```c 1178 ma_engine_play_sound(&engine, "my_sound.wav", pGroup); 1179 ``` 1180 1181 This is a "fire and forget" style of function. The engine will manage the `ma_sound` object 1182 internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility 1183 you'll want to initialize a sound object: 1184 1185 ```c 1186 ma_sound sound; 1187 1188 result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); 1189 if (result != MA_SUCCESS) { 1190 return result; // Failed to load sound. 1191 } 1192 ``` 1193 1194 Sounds need to be uninitialized with `ma_sound_uninit()`. 1195 1196 The example above loads a sound from a file. If the resource manager has been disabled you will not 1197 be able to use this function and instead you'll need to initialize a sound directly from a data 1198 source: 1199 1200 ```c 1201 ma_sound sound; 1202 1203 result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); 1204 if (result != MA_SUCCESS) { 1205 return result; 1206 } 1207 ``` 1208 1209 Each `ma_sound` object represents a single instance of the sound. If you want to play the same 1210 sound multiple times at the same time, you need to initialize a separate `ma_sound` object. 1211 1212 For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's 1213 standard config/init pattern: 1214 1215 ```c 1216 ma_sound sound; 1217 ma_sound_config soundConfig; 1218 1219 soundConfig = ma_sound_config_init(); 1220 soundConfig.pFilePath = NULL; // Set this to load from a file path. 1221 soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. 1222 soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; 1223 soundConfig.initialAttachmentInputBusIndex = 0; 1224 soundConfig.channelsIn = 1; 1225 soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. 1226 1227 result = ma_sound_init_ex(&soundConfig, &sound); 1228 if (result != MA_SUCCESS) { 1229 return result; 1230 } 1231 ``` 1232 1233 In the example above, the sound is being initialized without a file nor a data source. This is 1234 valid, in which case the sound acts as a node in the middle of the node graph. This means you can 1235 connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly 1236 what a `ma_sound_group` is. 1237 1238 When loading a sound, you specify a set of flags that control how the sound is loaded and what 1239 features are enabled for that sound. When no flags are set, the sound will be fully loaded into 1240 memory in exactly the same format as how it's stored on the file system. The resource manager will 1241 allocate a block of memory and then load the file directly into it. When reading audio data, it 1242 will be decoded dynamically on the fly. In order to save processing time on the audio thread, it 1243 might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: 1244 1245 ```c 1246 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); 1247 ``` 1248 1249 By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until 1250 the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously 1251 by specifying the `MA_SOUND_FLAG_ASYNC` flag: 1252 1253 ```c 1254 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); 1255 ``` 1256 1257 This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully 1258 loaded. When you start the sound, it won't output anything until some sound is available. The sound 1259 will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` 1260 is specified. 1261 1262 If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A 1263 fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal 1264 counter hit's zero. You can specify a fence like so: 1265 1266 ```c 1267 ma_result result; 1268 ma_fence fence; 1269 ma_sound sounds[4]; 1270 1271 result = ma_fence_init(&fence); 1272 if (result != MA_SUCCESS) { 1273 return result; 1274 } 1275 1276 // Load some sounds asynchronously. 1277 for (int iSound = 0; iSound < 4; iSound += 1) { 1278 ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); 1279 } 1280 1281 // ... do some other stuff here in the mean time ... 1282 1283 // Wait for all sounds to finish loading. 1284 ma_fence_wait(&fence); 1285 ``` 1286 1287 If loading the entire sound into memory is prohibitive, you can also configure the engine to stream 1288 the audio data: 1289 1290 ```c 1291 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); 1292 ``` 1293 1294 When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work 1295 fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music 1296 tracks in games. 1297 1298 When loading a sound from a file path, the engine will reference count the file to prevent it from 1299 being loaded if it's already in memory. When you uninitialize a sound, the reference count will be 1300 decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting 1301 system is not used for streams. The engine will use a 64-bit hash of the file name when comparing 1302 file paths which means there's a small chance you might encounter a name collision. If this is an 1303 issue, you'll need to use a different name for one of the colliding file paths, or just not load 1304 from files and instead load from a data source. 1305 1306 You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this 1307 only works for sounds that were initialized with `ma_sound_init_from_file()` and without the 1308 `MA_SOUND_FLAG_STREAM` flag. 1309 1310 When you initialize a sound, if you specify a sound group the sound will be attached to that group 1311 automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. 1312 If you would instead rather leave the sound unattached by default, you can can specify the 1313 `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node 1314 graph. 1315 1316 Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with 1317 `ma_sound_stop()`. 1318 1319 Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the 1320 engine's master volume. 1321 1322 Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan 1323 to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas 1324 +1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger 1325 value will result in a higher pitch. The pitch must be greater than 0. 1326 1327 The engine supports 3D spatialization of sounds. By default sounds will have spatialization 1328 enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways 1329 to disable spatialization of a sound: 1330 1331 ```c 1332 // Disable spatialization at initialization time via a flag: 1333 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); 1334 1335 // Dynamically disable or enable spatialization post-initialization: 1336 ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); 1337 ``` 1338 1339 By default sounds will be spatialized based on the closest listener. If a sound should always be 1340 spatialized relative to a specific listener it can be pinned to one: 1341 1342 ```c 1343 ma_sound_set_pinned_listener_index(&sound, listenerIndex); 1344 ``` 1345 1346 Like listeners, sounds have a position. By default, the position of a sound is in absolute space, 1347 but it can be changed to be relative to a listener: 1348 1349 ```c 1350 ma_sound_set_positioning(&sound, ma_positioning_relative); 1351 ``` 1352 1353 Note that relative positioning of a sound only makes sense if there is either only one listener, or 1354 the sound is pinned to a specific listener. To set the position of a sound: 1355 1356 ```c 1357 ma_sound_set_position(&sound, posX, posY, posZ); 1358 ``` 1359 1360 The direction works the same way as a listener and represents the sound's forward direction: 1361 1362 ```c 1363 ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); 1364 ``` 1365 1366 Sound's also have a cone for controlling directional attenuation. This works exactly the same as 1367 listeners: 1368 1369 ```c 1370 ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); 1371 ``` 1372 1373 The velocity of a sound is used for doppler effect and can be set as such: 1374 1375 ```c 1376 ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); 1377 ``` 1378 1379 The engine supports different attenuation models which can be configured on a per-sound basis. By 1380 default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to 1381 OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: 1382 1383 ```c 1384 ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); 1385 ``` 1386 1387 The supported attenuation models include the following: 1388 1389 +----------------------------------+----------------------------------------------+ 1390 | ma_attenuation_model_none | No distance attenuation. | 1391 +----------------------------------+----------------------------------------------+ 1392 | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | 1393 +----------------------------------+----------------------------------------------+ 1394 | ma_attenuation_model_linear | Linear attenuation. | 1395 +----------------------------------+----------------------------------------------+ 1396 | ma_attenuation_model_exponential | Exponential attenuation. | 1397 +----------------------------------+----------------------------------------------+ 1398 1399 To control how quickly a sound rolls off as it moves away from the listener, you need to configure 1400 the rolloff: 1401 1402 ```c 1403 ma_sound_set_rolloff(&sound, rolloff); 1404 ``` 1405 1406 You can control the minimum and maximum gain to apply from spatialization: 1407 1408 ```c 1409 ma_sound_set_min_gain(&sound, minGain); 1410 ma_sound_set_max_gain(&sound, maxGain); 1411 ``` 1412 1413 Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for 1414 the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain 1415 volume after the listener moves further away and to have sounds play a maximum volume when the 1416 listener is within a certain distance: 1417 1418 ```c 1419 ma_sound_set_min_distance(&sound, minDistance); 1420 ma_sound_set_max_distance(&sound, maxDistance); 1421 ``` 1422 1423 The engine's spatialization system supports doppler effect. The doppler factor can be configure on 1424 a per-sound basis like so: 1425 1426 ```c 1427 ma_sound_set_doppler_factor(&sound, dopplerFactor); 1428 ``` 1429 1430 You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and 1431 `ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the 1432 starting volume: 1433 1434 ```c 1435 // Fade in over 1 second. 1436 ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); 1437 1438 // ... sometime later ... 1439 1440 // Fade out over 1 second, starting from the current volume. 1441 ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); 1442 ``` 1443 1444 By default sounds will start immediately, but sometimes for timing and synchronization purposes it 1445 can be useful to schedule a sound to start or stop: 1446 1447 ```c 1448 // Start the sound in 1 second from now. 1449 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); 1450 1451 // Stop the sound in 2 seconds from now. 1452 ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); 1453 ``` 1454 1455 Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before 1456 anything will play. 1457 1458 The time is specified in global time which is controlled by the engine. You can get the engine's 1459 current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented 1460 automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` 1461 in case it needs to be resynchronized for some reason. 1462 1463 To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will 1464 take the scheduled start and stop times into account. 1465 1466 Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not 1467 be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. 1468 1469 Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping 1470 sound this should never return true. Alternatively, you can configure a callback that will be fired 1471 when the sound reaches the end. Note that the callback is fired from the audio thread which means 1472 you cannot be uninitializing sound from the callback. To set the callback you can use 1473 `ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it 1474 into the config like so: 1475 1476 ```c 1477 soundConfig.endCallback = my_end_callback; 1478 soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; 1479 ``` 1480 1481 The end callback is declared like so: 1482 1483 ```c 1484 void my_end_callback(void* pUserData, ma_sound* pSound) 1485 { 1486 ... 1487 } 1488 ``` 1489 1490 Internally a sound wraps around a data source. Some APIs exist to control the underlying data 1491 source, mainly for convenience: 1492 1493 ```c 1494 ma_sound_seek_to_pcm_frame(&sound, frameIndex); 1495 ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); 1496 ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); 1497 ma_sound_get_length_in_pcm_frames(&sound, &length); 1498 ``` 1499 1500 Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do 1501 not have any notion of a data source, anything relating to a data source is unavailable. 1502 1503 Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports 1504 file formats that have built-in support in miniaudio. You can extend this to support any kind of 1505 file format through the use of custom decoders. To do this you'll need to use a self-managed 1506 resource manager and configure it appropriately. See the "Resource Management" section below for 1507 details on how to set this up. 1508 1509 1510 6. Resource Management 1511 ====================== 1512 Many programs will want to manage sound resources for things such as reference counting and 1513 streaming. This is supported by miniaudio via the `ma_resource_manager` API. 1514 1515 The resource manager is mainly responsible for the following: 1516 1517 * Loading of sound files into memory with reference counting. 1518 * Streaming of sound data. 1519 1520 When loading a sound file, the resource manager will give you back a `ma_data_source` compatible 1521 object called `ma_resource_manager_data_source`. This object can be passed into any 1522 `ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you 1523 specify whether or not you want the sound to be fully loaded into memory (and optionally 1524 pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want 1525 the data to be loaded asynchronously. 1526 1527 The example below is how you can initialize a resource manager using it's default configuration: 1528 1529 ```c 1530 ma_resource_manager_config config; 1531 ma_resource_manager resourceManager; 1532 1533 config = ma_resource_manager_config_init(); 1534 result = ma_resource_manager_init(&config, &resourceManager); 1535 if (result != MA_SUCCESS) { 1536 ma_device_uninit(&device); 1537 printf("Failed to initialize the resource manager."); 1538 return -1; 1539 } 1540 ``` 1541 1542 You can configure the format, channels and sample rate of the decoded audio data. By default it 1543 will use the file's native data format, but you can configure it to use a consistent format. This 1544 is useful for offloading the cost of data conversion to load time rather than dynamically 1545 converting at mixing time. To do this, you configure the decoded format, channels and sample rate 1546 like the code below: 1547 1548 ```c 1549 config = ma_resource_manager_config_init(); 1550 config.decodedFormat = device.playback.format; 1551 config.decodedChannels = device.playback.channels; 1552 config.decodedSampleRate = device.sampleRate; 1553 ``` 1554 1555 In the code above, the resource manager will be configured so that any decoded audio data will be 1556 pre-converted at load time to the device's native data format. If instead you used defaults and 1557 the data format of the file did not match the device's data format, you would need to convert the 1558 data at mixing time which may be prohibitive in high-performance and large scale scenarios like 1559 games. 1560 1561 Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it 1562 only supports decoders that are built into miniaudio. It's possible to support additional encoding 1563 formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` 1564 vtables into the resource manager config: 1565 1566 ```c 1567 ma_decoding_backend_vtable* pCustomBackendVTables[] = 1568 { 1569 &g_ma_decoding_backend_vtable_libvorbis, 1570 &g_ma_decoding_backend_vtable_libopus 1571 }; 1572 1573 ... 1574 1575 resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; 1576 resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); 1577 resourceManagerConfig.pCustomDecodingBackendUserData = NULL; 1578 ``` 1579 1580 This system can allow you to support any kind of file format. See the "Decoding" section for 1581 details on how to implement custom decoders. The miniaudio repository includes examples for Opus 1582 via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. 1583 1584 Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the 1585 decoding of a page, a job will be posted to a queue which will then be processed by a job thread. 1586 By default there will be only one job thread running, but this can be configured, like so: 1587 1588 ```c 1589 config = ma_resource_manager_config_init(); 1590 config.jobThreadCount = MY_JOB_THREAD_COUNT; 1591 ``` 1592 1593 By default job threads are managed internally by the resource manager, however you can also self 1594 manage your job threads if, for example, you want to integrate the job processing into your 1595 existing job infrastructure, or if you simply don't like the way the resource manager does it. To 1596 do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first 1597 need to retrieve a job using `ma_resource_manager_next_job()` and then process it using 1598 `ma_job_process()`: 1599 1600 ```c 1601 config = ma_resource_manager_config_init(); 1602 config.jobThreadCount = 0; // Don't manage any job threads internally. 1603 config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. 1604 1605 // ... Initialize your custom job threads ... 1606 1607 void my_custom_job_thread(...) 1608 { 1609 for (;;) { 1610 ma_job job; 1611 ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); 1612 if (result != MA_SUCCESS) { 1613 if (result == MA_NO_DATA_AVAILABLE) { 1614 // No jobs are available. Keep going. Will only get this if the resource manager was initialized 1615 // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. 1616 continue; 1617 } else if (result == MA_CANCELLED) { 1618 // MA_JOB_TYPE_QUIT was posted. Exit. 1619 break; 1620 } else { 1621 // Some other error occurred. 1622 break; 1623 } 1624 } 1625 1626 ma_job_process(&job); 1627 } 1628 } 1629 ``` 1630 1631 In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination 1632 indicator, but you can use whatever you would like to terminate the thread. The call to 1633 `ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking 1634 by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration 1635 flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This 1636 is to give every thread the opportunity to catch the event and terminate naturally. 1637 1638 When loading a file, it's sometimes convenient to be able to customize how files are opened and 1639 read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by 1640 default. This can be done by setting `pVFS` member of the resource manager's config: 1641 1642 ```c 1643 // Initialize your custom VFS object. See documentation for VFS for information on how to do this. 1644 my_custom_vfs vfs = my_custom_vfs_init(); 1645 1646 config = ma_resource_manager_config_init(); 1647 config.pVFS = &vfs; 1648 ``` 1649 1650 This is particularly useful in programs like games where you want to read straight from an archive 1651 rather than the normal file system. If you do not specify a custom VFS, the resource manager will 1652 use the operating system's normal file operations. 1653 1654 To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When 1655 loading a sound you need to specify the file path and options for how the sounds should be loaded. 1656 By default a sound will be loaded synchronously. The returned data source is owned by the caller 1657 which means the caller is responsible for the allocation and freeing of the data source. Below is 1658 an example for initializing a data source: 1659 1660 ```c 1661 ma_resource_manager_data_source dataSource; 1662 ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); 1663 if (result != MA_SUCCESS) { 1664 // Error. 1665 } 1666 1667 // ... 1668 1669 // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call 1670 // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. 1671 result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); 1672 if (result != MA_SUCCESS) { 1673 // Failed to read PCM frames. 1674 } 1675 1676 // ... 1677 1678 ma_resource_manager_data_source_uninit(&dataSource); 1679 ``` 1680 1681 The `flags` parameter specifies how you want to perform loading of the sound file. It can be a 1682 combination of the following flags: 1683 1684 ``` 1685 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM 1686 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE 1687 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC 1688 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT 1689 ``` 1690 1691 When no flags are specified (set to 0), the sound will be fully loaded into memory, but not 1692 decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when 1693 `ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in 1694 memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will 1695 be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after 1696 the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You 1697 can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. 1698 This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be 1699 returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is 1700 available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by 1701 `ma_data_source_read_pcm_frames()`. 1702 1703 For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you 1704 can instead stream audio data which you can do by specifying the 1705 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 1706 second pages. When a new page needs to be decoded, a job will be posted to the job queue and then 1707 subsequently processed in a job thread. 1708 1709 For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means 1710 multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in 1711 the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be 1712 matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful 1713 for a program to register self-managed raw audio data and associate it with a file path. Use the 1714 `ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. 1715 `ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed 1716 decoded audio data in the specified data format with the specified name. Likewise, 1717 `ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed 1718 encoded audio data (the raw file data) with the specified name. Note that these names need not be 1719 actual file paths. When `ma_resource_manager_data_source_init()` is called (without the 1720 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these 1721 explicitly registered data buffers and, if found, will use it as the backing data for the data 1722 source. Note that the resource manager does *not* make a copy of this data so it is up to the 1723 caller to ensure the pointer stays valid for it's lifetime. Use 1724 `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use 1725 `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and 1726 unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` 1727 flag with a self-managed data pointer. 1728 1729 1730 6.1. Asynchronous Loading and Synchronization 1731 --------------------------------------------- 1732 When loading asynchronously, it can be useful to poll whether or not loading has finished. Use 1733 `ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will 1734 return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, 1735 `MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed 1736 to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been 1737 decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` 1738 will be returned. Otherwise, some other error code will be returned if the sound failed to load. 1739 1740 In addition to polling, you can also use a simple synchronization object called a "fence" to wait 1741 for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a 1742 fence is that it can be used to wait for a group of sounds to finish loading rather than waiting 1743 for sounds on an individual basis. There are two stages to loading a sound: 1744 1745 * Initialization of the internal decoder; and 1746 * Completion of decoding of the file (the file is fully decoded) 1747 1748 You can specify separate fences for each of the different stages. Waiting for the initialization 1749 of the internal decoder is important for when you need to know the sample format, channels and 1750 sample rate of the file. 1751 1752 The example below shows how you could use a fence when loading a number of sounds: 1753 1754 ```c 1755 // This fence will be released when all sounds are finished loading entirely. 1756 ma_fence fence; 1757 ma_fence_init(&fence); 1758 1759 // This will be passed into the initialization routine for each sound. 1760 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); 1761 notifications.done.pFence = &fence; 1762 1763 // Now load a bunch of sounds: 1764 for (iSound = 0; iSound < soundCount; iSound += 1) { 1765 ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); 1766 } 1767 1768 // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... 1769 1770 // Wait for loading of sounds to finish. 1771 ma_fence_wait(&fence); 1772 ``` 1773 1774 In the example above we used a fence for waiting until the entire file has been fully decoded. If 1775 you only need to wait for the initialization of the internal decoder to complete, you can use the 1776 `init` member of the `ma_resource_manager_pipeline_notifications` object: 1777 1778 ```c 1779 notifications.init.pFence = &fence; 1780 ``` 1781 1782 If a fence is not appropriate for your situation, you can instead use a callback that is fired on 1783 an individual sound basis. This is done in a very similar way to fences: 1784 1785 ```c 1786 typedef struct 1787 { 1788 ma_async_notification_callbacks cb; 1789 void* pMyData; 1790 } my_notification; 1791 1792 void my_notification_callback(ma_async_notification* pNotification) 1793 { 1794 my_notification* pMyNotification = (my_notification*)pNotification; 1795 1796 // Do something in response to the sound finishing loading. 1797 } 1798 1799 ... 1800 1801 my_notification myCallback; 1802 myCallback.cb.onSignal = my_notification_callback; 1803 myCallback.pMyData = pMyData; 1804 1805 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); 1806 notifications.done.pNotification = &myCallback; 1807 1808 ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); 1809 ``` 1810 1811 In the example above we just extend the `ma_async_notification_callbacks` object and pass an 1812 instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with 1813 the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same 1814 time and they should both work as expected. If using the `pNotification` system, you need to ensure 1815 your `ma_async_notification_callbacks` object stays valid. 1816 1817 1818 1819 6.2. Resource Manager Implementation Details 1820 -------------------------------------------- 1821 Resources are managed in two main ways: 1822 1823 * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) 1824 * By streaming audio data on the fly (referred to as a data stream) 1825 1826 A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or 1827 data stream, depending on whether or not the data source was initialized with the 1828 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a 1829 `ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` 1830 object. Both of these objects are data sources which means they can be used with any 1831 `ma_data_source_*()` API. 1832 1833 Another major feature of the resource manager is the ability to asynchronously decode audio files. 1834 This relieves the audio thread of time-consuming decoding which can negatively affect scalability 1835 due to the audio thread needing to complete it's work extremely quickly to avoid glitching. 1836 Asynchronous decoding is achieved through a job system. There is a central multi-producer, 1837 multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is 1838 posted to the queue which is then read by a job thread. The number of job threads can be 1839 configured for improved scalability, and job threads can all run in parallel without needing to 1840 worry about the order of execution (how this is achieved is explained below). 1841 1842 When a sound is being loaded asynchronously, playback can begin before the sound has been fully 1843 decoded. This enables the application to start playback of the sound quickly, while at the same 1844 time allowing to resource manager to keep loading in the background. Since there may be less 1845 threads than the number of sounds being loaded at a given time, a simple scheduling system is used 1846 to keep decoding time balanced and fair. The resource manager solves this by splitting decoding 1847 into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a 1848 new job will be posted to start decoding the next page. By dividing up decoding into pages, an 1849 individual sound shouldn't ever delay every other sound from having their first page decoded. Of 1850 course, when loading many sounds at the same time, there will always be an amount of time required 1851 to process jobs in the queue so in heavy load situations there will still be some delay. To 1852 determine if a data source is ready to have some frames read, use 1853 `ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames 1854 available starting from the current position. 1855 1856 1857 6.2.1. Job Queue 1858 ---------------- 1859 The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. 1860 This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. 1861 Only a fixed number of jobs can be allocated and inserted into the queue which is done through a 1862 lock-free data structure for allocating an index into a fixed sized array, with reference counting 1863 for mitigation of the ABA problem. The reference count is 32-bit. 1864 1865 For many types of jobs it's important that they execute in a specific order. In these cases, jobs 1866 are executed serially. For the resource manager, serial execution of jobs is only required on a 1867 per-object basis (per data buffer or per data stream). Each of these objects stores an execution 1868 counter. When a job is posted it is associated with an execution counter. When the job is 1869 processed, it checks if the execution counter of the job equals the execution counter of the 1870 owning object and if so, processes the job. If the counters are not equal, the job will be posted 1871 back onto the job queue for later processing. When the job finishes processing the execution order 1872 of the main object is incremented. This system means the no matter how many job threads are 1873 executing, decoding of an individual sound will always get processed serially. The advantage to 1874 having multiple threads comes into play when loading multiple sounds at the same time. 1875 1876 The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve 1877 thread-safety for a very small section of code. This is only relevant when the resource manager 1878 uses more than one job thread. If only using a single job thread, which is the default, the 1879 lock should never actually wait in practice. The amount of time spent locking should be quite 1880 short, but it's something to be aware of for those who have pedantic lock-free requirements and 1881 need to use more than one job thread. There are plans to remove this lock in a future version. 1882 1883 In addition, posting a job will release a semaphore, which on Win32 is implemented with 1884 `ReleaseSemaphore` and on POSIX platforms via a condition variable: 1885 1886 ```c 1887 pthread_mutex_lock(&pSemaphore->lock); 1888 { 1889 pSemaphore->value += 1; 1890 pthread_cond_signal(&pSemaphore->cond); 1891 } 1892 pthread_mutex_unlock(&pSemaphore->lock); 1893 ``` 1894 1895 Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid 1896 this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` 1897 flag) and implement your own job processing routine (see the "Resource Manager" section above for 1898 details on how to do this). 1899 1900 1901 1902 6.2.2. Data Buffers 1903 ------------------- 1904 When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the 1905 resource manager will try to load the data into an in-memory data buffer. Before doing so, however, 1906 it will first check if the specified file is already loaded. If so, it will increment a reference 1907 counter and just use the already loaded data. This saves both time and memory. When the data buffer 1908 is uninitialized, the reference counter will be decremented. If the counter hits zero, the file 1909 will be unloaded. This is a detail to keep in mind because it could result in excessive loading and 1910 unloading of a sound. For example, the following sequence will result in a file be loaded twice, 1911 once after the other: 1912 1913 ```c 1914 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. 1915 ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. 1916 1917 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. 1918 ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. 1919 ``` 1920 1921 A binary search tree (BST) is used for storing data buffers as it has good balance between 1922 efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed 1923 into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves 1924 memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST 1925 due to the random nature of the hash. The disadvantages are that file names are case-sensitive and 1926 there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize 1927 your file names to upper- or lower-case before initializing your data sources. If name collisions 1928 become an issue, you'll need to change the name of one of the colliding names or just not use the 1929 resource manager. 1930 1931 When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` 1932 flag is excluded, the file will be decoded synchronously by the calling thread. There are two 1933 options for controlling how the audio is stored in the data buffer - encoded or decoded. When the 1934 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored 1935 in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is 1936 a very simple and standard process of simply adding an item to the BST, allocating a block of 1937 memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). 1938 1939 When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer 1940 is done asynchronously. In this case, a job is posted to the queue to start loading and then the 1941 function immediately returns, setting an internal result code to `MA_BUSY`. This result code is 1942 returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully 1943 completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. 1944 1945 When loading asynchronously, a single job is posted to the queue of the type 1946 `MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and 1947 associating it with job. When the job is processed by the job thread, it will first load the file 1948 using the VFS associated with the resource manager. When using a custom VFS, it's important that it 1949 be completely thread-safe because it will be used from one or more job threads at the same time. 1950 Individual files should only ever be accessed by one thread at a time, however. After opening the 1951 file via the VFS, the job will determine whether or not the file is being decoded. If not, it 1952 simply allocates a block of memory and loads the raw file contents into it and returns. On the 1953 other hand, when the file is being decoded, it will first allocate a decoder on the heap and 1954 initialize it. Then it will check if the length of the file is known. If so it will allocate a 1955 block of memory to store the decoded output and initialize it to silence. If the size is unknown, 1956 it will allocate room for one page. After memory has been allocated, the first page will be 1957 decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the 1958 completion event will be signalled and loading is now complete. If, however, there is more to 1959 decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job 1960 will decode the next page and perform the same process if it reaches the end. If there is more to 1961 decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will 1962 keep on happening until the sound has been fully decoded. For sounds of an unknown length, each 1963 page will be linked together as a linked list. Internally this is implemented via the 1964 `ma_paged_audio_buffer` object. 1965 1966 1967 6.2.3. Data Streams 1968 ------------------- 1969 Data streams only ever store two pages worth of data for each instance. They are most useful for 1970 large sounds like music tracks in games that would consume too much memory if fully decoded in 1971 memory. After every frame from a page has been read, a job will be posted to load the next page 1972 which is done from the VFS. 1973 1974 For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or 1975 not initialization of the data source waits until the two pages have been decoded. When unset, 1976 `ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise 1977 it will return immediately. 1978 1979 When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, 1980 `MA_BUSY` will be returned if there are no frames available. If there are some frames available, 1981 but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames 1982 read will be less than the number requested. Due to the asynchronous nature of data streams, 1983 seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be 1984 returned when trying to read frames. 1985 1986 When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed 1987 a job is posted to load the next page. This will be posted from the same thread that called 1988 `ma_resource_manager_data_source_read_pcm_frames()`. 1989 1990 Data streams are uninitialized by posting a job to the queue, but the function won't return until 1991 that job has been processed. The reason for this is that the caller owns the data stream object and 1992 therefore miniaudio needs to ensure everything completes before handing back control to the caller. 1993 Also, if the data stream is uninitialized while pages are in the middle of decoding, they must 1994 complete before destroying any underlying object and the job system handles this cleanly. 1995 1996 Note that when a new page needs to be loaded, a job will be posted to the resource manager's job 1997 thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" 1998 section above regarding locking when posting an event if you require a strictly lock-free audio 1999 thread. 2000 2001 2002 2003 7. Node Graph 2004 ============= 2005 miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a 2006 node whose outputs are attached to inputs of another node, thereby creating a graph. There are 2007 different types of nodes, with each node in the graph processing input data to produce output, 2008 which is then fed through the chain. Each node in the graph can apply their own custom effects. At 2009 the start of the graph will usually be one or more data source nodes which have no inputs and 2010 instead pull their data from a data source. At the end of the graph is an endpoint which represents 2011 the end of the chain and is where the final output is ultimately extracted from. 2012 2013 Each node has a number of input buses and a number of output buses. An output bus from a node is 2014 attached to an input bus of another. Multiple nodes can connect their output buses to another 2015 node's input bus, in which case their outputs will be mixed before processing by the node. Below is 2016 a diagram that illustrates a hypothetical node graph setup: 2017 2018 ``` 2019 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2020 2021 +---------------+ +-----------------+ 2022 | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ 2023 +---------------+ | | =----+ +-----------------+ | +----------+ 2024 +----= Splitter | +----= ENDPOINT | 2025 +---------------+ | | =----+ +-----------------+ | +----------+ 2026 | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ 2027 +---------------+ +-----------------+ 2028 ``` 2029 2030 In the above graph, it starts with two data sources whose outputs are attached to the input of a 2031 splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter 2032 performs it's processing routine and produces two outputs which is simply a duplication of the 2033 input stream. One output is attached to a low pass filter, whereas the other output is attached to 2034 a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and 2035 since they're both connected to the same input bus, they'll be mixed. 2036 2037 Each input bus must be configured to accept the same number of channels, but the number of channels 2038 used by input buses can be different to the number of channels for output buses in which case 2039 miniaudio will automatically convert the input data to the output channel count before processing. 2040 The number of channels of an output bus of one node must match the channel count of the input bus 2041 it's attached to. The channel counts cannot be changed after the node has been initialized. If you 2042 attempt to attach an output bus to an input bus with a different channel count, attachment will 2043 fail. 2044 2045 To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a 2046 container around the entire graph. The `ma_node_graph` object is required for some thread-safety 2047 issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's 2048 standard config/init system: 2049 2050 ```c 2051 ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); 2052 2053 result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. 2054 if (result != MA_SUCCESS) { 2055 // Failed to initialize node graph. 2056 } 2057 ``` 2058 2059 When you initialize the node graph, you're specifying the channel count of the endpoint. The 2060 endpoint is a special node which has one input bus and one output bus, both of which have the 2061 same channel count, which is specified in the config. Any nodes that connect directly to the 2062 endpoint must be configured such that their output buses have the same channel count. When you read 2063 audio data from the node graph, it'll have the channel count you specified in the config. To read 2064 data from the graph: 2065 2066 ```c 2067 ma_uint32 framesRead; 2068 result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); 2069 if (result != MA_SUCCESS) { 2070 // Failed to read data from the node graph. 2071 } 2072 ``` 2073 2074 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in 2075 data from it's input attachments, which in turn recursively pull in data from their inputs, and so 2076 on. At the start of the graph there will be some kind of data source node which will have zero 2077 inputs and will instead read directly from a data source. The base nodes don't literally need to 2078 read from a `ma_data_source` object, but they will always have some kind of underlying object that 2079 sources some kind of audio. The `ma_data_source_node` node can be used to read from a 2080 `ma_data_source`. Data is always in floating-point format and in the number of channels you 2081 specified when the graph was initialized. The sample rate is defined by the underlying data sources. 2082 It's up to you to ensure they use a consistent and appropriate sample rate. 2083 2084 The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but 2085 miniaudio includes a few stock nodes for common functionality. This is how you would initialize a 2086 node which reads directly from a data source (`ma_data_source_node`) which is an example of one 2087 of the stock nodes that comes with miniaudio: 2088 2089 ```c 2090 ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); 2091 2092 ma_data_source_node dataSourceNode; 2093 result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); 2094 if (result != MA_SUCCESS) { 2095 // Failed to create data source node. 2096 } 2097 ``` 2098 2099 The data source node will use the output channel count to determine the channel count of the output 2100 bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data 2101 source). The data source must output to floating-point (`ma_format_f32`) or else an error will be 2102 returned from `ma_data_source_node_init()`. 2103 2104 By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: 2105 2106 ```c 2107 result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); 2108 if (result != MA_SUCCESS) { 2109 // Failed to attach node. 2110 } 2111 ``` 2112 2113 The code above connects the data source node directly to the endpoint. Since the data source node 2114 has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single 2115 input bus which means the input bus index will also always be 0. 2116 2117 To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use 2118 `ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to 2119 another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll 2120 deal with it for you. 2121 2122 Less frequently you may want to create a specialized node. This will be a node where you implement 2123 your own processing callback to apply a custom effect of some kind. This is similar to initializing 2124 one of the stock node types, only this time you need to specify a pointer to a vtable containing a 2125 pointer to the processing function and the number of input and output buses. Example: 2126 2127 ```c 2128 static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 2129 { 2130 // Do some processing of ppFramesIn (one stream of audio data per input bus) 2131 const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. 2132 const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. 2133 float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. 2134 2135 // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each 2136 // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers 2137 // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames 2138 // your node consumed and `pFrameCountOut` should be set the number of output frames that 2139 // were produced. 2140 // 2141 // You should process as many frames as you can. If your effect consumes input frames at the 2142 // same rate as output frames (always the case, unless you're doing resampling), you need 2143 // only look at `ppFramesOut` and process that exact number of frames. If you're doing 2144 // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` 2145 // properly. 2146 } 2147 2148 static ma_node_vtable my_custom_node_vtable = 2149 { 2150 my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. 2151 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. 2152 2, // 2 input buses. 2153 1, // 1 output bus. 2154 0 // Default flags. 2155 }; 2156 2157 ... 2158 2159 // Each bus needs to have a channel count specified. To do this you need to specify the channel 2160 // counts in an array and then pass that into the node config. 2161 ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. 2162 ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. 2163 2164 inputChannels[0] = channelsIn; 2165 inputChannels[1] = channelsIn; 2166 outputChannels[0] = channelsOut; 2167 2168 ma_node_config nodeConfig = ma_node_config_init(); 2169 nodeConfig.vtable = &my_custom_node_vtable; 2170 nodeConfig.pInputChannels = inputChannels; 2171 nodeConfig.pOutputChannels = outputChannels; 2172 2173 ma_node_base node; 2174 result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); 2175 if (result != MA_SUCCESS) { 2176 // Failed to initialize node. 2177 } 2178 ``` 2179 2180 When initializing a custom node, as in the code above, you'll normally just place your vtable in 2181 static space. The number of input and output buses are specified as part of the vtable. If you need 2182 a variable number of buses on a per-node bases, the vtable should have the relevant bus count set 2183 to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: 2184 2185 ```c 2186 static ma_node_vtable my_custom_node_vtable = 2187 { 2188 my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. 2189 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. 2190 MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. 2191 1, // 1 output bus. 2192 0 // Default flags. 2193 }; 2194 2195 ... 2196 2197 ma_node_config nodeConfig = ma_node_config_init(); 2198 nodeConfig.vtable = &my_custom_node_vtable; 2199 nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. 2200 nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. 2201 nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. 2202 ``` 2203 2204 In the above example it's important to never set the `inputBusCount` and `outputBusCount` members 2205 to anything other than their defaults if the vtable specifies an explicit count. They can only be 2206 set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. 2207 2208 Most often you'll want to create a structure to encapsulate your node with some extra data. You 2209 need to make sure the `ma_node_base` object is your first member of the structure: 2210 2211 ```c 2212 typedef struct 2213 { 2214 ma_node_base base; // <-- Make sure this is always the first member. 2215 float someCustomData; 2216 } my_custom_node; 2217 ``` 2218 2219 By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the 2220 graph just like any other node. 2221 2222 In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the 2223 number of channels for each bus is what was specified by the config when the node was initialized 2224 with `ma_node_init()`. In addition, all attachments to each of the input buses will have been 2225 pre-mixed by miniaudio. The config allows you to specify different channel counts for each 2226 individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, 2227 return an error in it's initialization routine. 2228 2229 Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable 2230 and include the following: 2231 2232 +-----------------------------------------+---------------------------------------------------+ 2233 | Flag Name | Description | 2234 +-----------------------------------------+---------------------------------------------------+ 2235 | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | 2236 | | processing, but are instead used for tracking | 2237 | | time, handling events, etc. Also used by the | 2238 | | internal endpoint node. It reads directly from | 2239 | | the input bus to the output bus. Nodes with this | 2240 | | flag must have exactly 1 input bus and 1 output | 2241 | | bus, and both buses must have the same channel | 2242 | | counts. | 2243 +-----------------------------------------+---------------------------------------------------+ 2244 | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | 2245 | | when no data is available to be read from input | 2246 | | attachments. When a node has at least one input | 2247 | | bus, but there are no inputs attached or the | 2248 | | inputs do not deliver any data, the node's | 2249 | | processing callback will not get fired. This flag | 2250 | | will make it so the callback is always fired | 2251 | | regardless of whether or not any input data is | 2252 | | received. This is useful for effects like | 2253 | | echos where there will be a tail of audio data | 2254 | | that still needs to be processed even when the | 2255 | | original data sources have reached their ends. It | 2256 | | may also be useful for nodes that must always | 2257 | | have their processing callback fired when there | 2258 | | are no inputs attached. | 2259 +-----------------------------------------+---------------------------------------------------+ 2260 | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | 2261 | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | 2262 | | is set, the `ppFramesIn` parameter of the | 2263 | | processing callback will be set to NULL when | 2264 | | there are no input frames are available. When | 2265 | | this is unset, silence will be posted to the | 2266 | | processing callback. | 2267 +-----------------------------------------+---------------------------------------------------+ 2268 | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | 2269 | | frames are processed at different rates. You | 2270 | | should set this for any nodes that perform | 2271 | | resampling. | 2272 +-----------------------------------------+---------------------------------------------------+ 2273 | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | 2274 | | silent output. This is useful for nodes where you | 2275 | | don't want the output to contribute to the final | 2276 | | mix. An example might be if you want split your | 2277 | | stream and have one branch be output to a file. | 2278 | | When using this flag, you should avoid writing to | 2279 | | the output buffer of the node's processing | 2280 | | callback because miniaudio will ignore it anyway. | 2281 +-----------------------------------------+---------------------------------------------------+ 2282 2283 2284 If you need to make a copy of an audio stream for effect processing you can use a splitter node 2285 called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. 2286 You can use it like this: 2287 2288 ```c 2289 ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); 2290 2291 ma_splitter_node splitterNode; 2292 result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); 2293 if (result != MA_SUCCESS) { 2294 // Failed to create node. 2295 } 2296 2297 // Attach your output buses to two different input buses (can be on two different nodes). 2298 ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. 2299 ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. 2300 ``` 2301 2302 The volume of an output bus can be configured on a per-bus basis: 2303 2304 ```c 2305 ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); 2306 ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); 2307 ``` 2308 2309 In the code above we're using the splitter node from before and changing the volume of each of the 2310 copied streams. 2311 2312 You can start and stop a node with the following: 2313 2314 ```c 2315 ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. 2316 ma_node_set_state(&splitterNode, ma_node_state_stopped); 2317 ``` 2318 2319 By default the node is in a started state, but since it won't be connected to anything won't 2320 actually be invoked by the node graph until it's connected. When you stop a node, data will not be 2321 read from any of it's input connections. You can use this property to stop a group of sounds 2322 atomically. 2323 2324 You can configure the initial state of a node in it's config: 2325 2326 ```c 2327 nodeConfig.initialState = ma_node_state_stopped; 2328 ``` 2329 2330 Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member 2331 which is the config to use with the base node. This is where the initial state can be configured 2332 for specialized nodes: 2333 2334 ```c 2335 dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; 2336 ``` 2337 2338 When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not 2339 modify the `vtable` member of the `nodeConfig` object. 2340 2341 2342 7.1. Timing 2343 ----------- 2344 The node graph supports starting and stopping nodes at scheduled times. This is especially useful 2345 for data source nodes where you want to get the node set up, but only start playback at a specific 2346 time. There are two clocks: local and global. 2347 2348 A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can 2349 only be done based on the global clock because the local clock will not be running while the node 2350 is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the 2351 other hand, the local clock only advances when the node's processing callback is fired, and is 2352 advanced based on the output frame count. 2353 2354 To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with 2355 `ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. 2356 Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, 2357 and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the 2358 audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these 2359 outside of the node processing callbacks which are always run on the audio thread. 2360 2361 There is basic support for scheduling the starting and stopping of nodes. You can only schedule one 2362 start and one stop at a time. This is mainly intended for putting nodes into a started or stopped 2363 state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited 2364 to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks 2365 of several milliseconds. The following APIs can be used for scheduling node states: 2366 2367 ```c 2368 ma_node_set_state_time() 2369 ma_node_get_state_time() 2370 ``` 2371 2372 The time is absolute and must be based on the global clock. An example is below: 2373 2374 ```c 2375 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. 2376 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. 2377 ``` 2378 2379 An example for changing the state using a relative time. 2380 2381 ```c 2382 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); 2383 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); 2384 ``` 2385 2386 Note that due to the nature of multi-threading the times may not be 100% exact. If this is an 2387 issue, consider scheduling state changes from within a processing callback. An idea might be to 2388 have some kind of passthrough trigger node that is used specifically for tracking time and handling 2389 events. 2390 2391 2392 2393 7.2. Thread Safety and Locking 2394 ------------------------------ 2395 When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's 2396 expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so 2397 without the use of any locks. This section discusses the implementation used by miniaudio and goes 2398 over some of the compromises employed by miniaudio to achieve this goal. Note that the current 2399 implementation may not be ideal - feedback and critiques are most welcome. 2400 2401 The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected 2402 to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the 2403 implementation, but are crafted in a way such that such locking is not required when reading audio 2404 data from the graph. Locking in these areas are achieved by means of spinlocks. 2405 2406 The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact 2407 that a node can be uninitialized, and it's memory potentially freed, while in the middle of being 2408 processed on the audio thread. There are times when the audio thread will be referencing a node, 2409 which means the uninitialization process of a node needs to make sure it delays returning until the 2410 audio thread is finished so that control is not handed back to the caller thereby giving them a 2411 chance to free the node's memory. 2412 2413 When the audio thread is processing a node, it does so by reading from each of the output buses of 2414 the node. In order for a node to process data for one of it's output buses, it needs to read from 2415 each of it's input buses, and so on an so forth. It follows that once all output buses of a node 2416 are detached, the node as a whole will be disconnected and no further processing will occur unless 2417 it's output buses are reattached, which won't be happening when the node is being uninitialized. 2418 By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can 2419 simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By 2420 doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output 2421 nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean 2422 up. 2423 2424 With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as 2425 it takes to process the output bus being detached. This will happen if it's called at just the 2426 wrong moment where the audio thread has just iterated it and has just started processing. The 2427 caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which 2428 includes the cost of recursively processing it's inputs. This is the biggest compromise made with 2429 the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes 2430 earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching 2431 higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass 2432 detachments, detach starting from the lowest level nodes and work your way towards the final 2433 endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not 2434 running, detachment will be fast and detachment in any order will be the same. The reason nodes 2435 need to wait for their input attachments to complete is due to the potential for desyncs between 2436 data sources. If the node was to terminate processing mid way through processing it's inputs, 2437 there's a chance that some of the underlying data sources will have been read, but then others not. 2438 That will then result in a potential desynchronization when detaching and reattaching higher-level 2439 nodes. A possible solution to this is to have an option when detaching to terminate processing 2440 before processing all input attachments which should be fairly simple. 2441 2442 Another compromise, albeit less significant, is locking when attaching and detaching nodes. This 2443 locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present 2444 for each input bus and output bus. When an output bus is connected to an input bus, both the output 2445 bus and input bus is locked. This locking is specifically for attaching and detaching across 2446 different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and 2447 unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when 2448 considering that iterating over attachments must not break as a result of attaching or detaching a 2449 node while iteration is occurring. 2450 2451 Attaching and detaching are both quite simple. When an output bus of a node is attached to an input 2452 bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where 2453 each item in the list is and output bus. We have some intentional (and convenient) restrictions on 2454 what can done with the linked list in order to simplify the implementation. First of all, whenever 2455 something needs to iterate over the list, it must do so in a forward direction. Backwards iteration 2456 is not supported. Also, items can only be added to the start of the list. 2457 2458 The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer 2459 to the next item in the list, and another to the previous item. A pointer to the previous item is 2460 only required for fast detachment of the node - it is never used in iteration. This is an 2461 important property because it means from the perspective of iteration, attaching and detaching of 2462 an item can be done with a single atomic assignment. This is exploited by both the attachment and 2463 detachment process. When attaching the node, the first thing that is done is the setting of the 2464 local "next" and "previous" pointers of the node. After that, the item is "attached" to the list 2465 by simply performing an atomic exchange with the head pointer. After that, the node is "attached" 2466 to the list from the perspective of iteration. Even though the "previous" pointer of the next item 2467 hasn't yet been set, from the perspective of iteration it's been attached because iteration will 2468 only be happening in a forward direction which means the "previous" pointer won't actually ever get 2469 used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and 2470 `ma_node_detach_output_bus()` for the implementation of this mechanism. 2471 2472 2473 2474 8. Decoding 2475 =========== 2476 The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from 2477 devices and can be used independently. Built-in support is included for the following formats: 2478 2479 +---------+ 2480 | Format | 2481 +---------+ 2482 | WAV | 2483 | MP3 | 2484 | FLAC | 2485 +---------+ 2486 2487 You can disable the built-in decoders by specifying one or more of the following options before the 2488 miniaudio implementation: 2489 2490 ```c 2491 #define MA_NO_WAV 2492 #define MA_NO_MP3 2493 #define MA_NO_FLAC 2494 ``` 2495 2496 miniaudio supports the ability to plug in custom decoders. See the section below for details on how 2497 to use custom decoders. 2498 2499 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with 2500 `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is 2501 an example for loading a decoder from a file: 2502 2503 ```c 2504 ma_decoder decoder; 2505 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); 2506 if (result != MA_SUCCESS) { 2507 return false; // An error occurred. 2508 } 2509 2510 ... 2511 2512 ma_decoder_uninit(&decoder); 2513 ``` 2514 2515 When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object 2516 (the `NULL` argument in the example above) which allows you to configure the output format, channel 2517 count, sample rate and channel map: 2518 2519 ```c 2520 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); 2521 ``` 2522 2523 When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the 2524 same as that defined by the decoding backend. 2525 2526 Data is read from the decoder as PCM frames. This will output the number of PCM frames actually 2527 read. If this is less than the requested number of PCM frames it means you've reached the end. The 2528 return value will be `MA_AT_END` if no samples have been read and the end has been reached. 2529 2530 ```c 2531 ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); 2532 if (framesRead < framesToRead) { 2533 // Reached the end. 2534 } 2535 ``` 2536 2537 You can also seek to a specific frame like so: 2538 2539 ```c 2540 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); 2541 if (result != MA_SUCCESS) { 2542 return false; // An error occurred. 2543 } 2544 ``` 2545 2546 If you want to loop back to the start, you can simply seek back to the first PCM frame: 2547 2548 ```c 2549 ma_decoder_seek_to_pcm_frame(pDecoder, 0); 2550 ``` 2551 2552 When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding 2553 backend. This can be unnecessarily inefficient if the type is already known. In this case you can 2554 use `encodingFormat` variable in the device config to specify a specific encoding format you want 2555 to decode: 2556 2557 ```c 2558 decoderConfig.encodingFormat = ma_encoding_format_wav; 2559 ``` 2560 2561 See the `ma_encoding_format` enum for possible encoding formats. 2562 2563 The `ma_decoder_init_file()` API will try using the file extension to determine which decoding 2564 backend to prefer. 2565 2566 2567 8.1. Custom Decoders 2568 -------------------- 2569 It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful 2570 when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of 2571 the stock formats supported by miniaudio. This can be put to particularly good use when using the 2572 `ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for 2573 example, you wanted to support Opus, you can do so with a custom decoder (there if a reference 2574 Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). 2575 2576 A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs 2577 to be implemented which is then passed into the decoder config: 2578 2579 ```c 2580 ma_decoding_backend_vtable* pCustomBackendVTables[] = 2581 { 2582 &g_ma_decoding_backend_vtable_libvorbis, 2583 &g_ma_decoding_backend_vtable_libopus 2584 }; 2585 2586 ... 2587 2588 decoderConfig = ma_decoder_config_init_default(); 2589 decoderConfig.pCustomBackendUserData = NULL; 2590 decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; 2591 decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); 2592 ``` 2593 2594 The `ma_decoding_backend_vtable` vtable has the following functions: 2595 2596 ``` 2597 onInit 2598 onInitFile 2599 onInitFileW 2600 onInitMemory 2601 onUninit 2602 ``` 2603 2604 There are only two functions that must be implemented - `onInit` and `onUninit`. The other 2605 functions can be implemented for a small optimization for loading from a file path or memory. If 2606 these are not specified, miniaudio will deal with it for you via a generic implementation. 2607 2608 When you initialize a custom data source (by implementing the `onInit` function in the vtable) you 2609 will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the 2610 section about data sources for details on how to implement this. Alternatively, see the 2611 "custom_decoders" example in the miniaudio repository. 2612 2613 The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data 2614 from some arbitrary source. You'll use these functions to read from the raw data and perform the 2615 decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant 2616 parameter. 2617 2618 The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only 2619 used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, 2620 an optimal implementation will handle the relevant properties appropriately. 2621 2622 If memory allocation is required, it should be done so via the specified allocation callbacks if 2623 possible (the `pAllocationCallbacks` parameter). 2624 2625 If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to 2626 NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. 2627 When multiple custom backends are specified, miniaudio will cycle through the vtables in the order 2628 they're listed in the array that's passed into the decoder config so it's important that your 2629 initialization routine is clean. 2630 2631 When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an 2632 opportunity to clean up and internal data. 2633 2634 2635 2636 9. Encoding 2637 =========== 2638 The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. 2639 This can be disabled by specifying the following option before the implementation of miniaudio: 2640 2641 ```c 2642 #define MA_NO_WAV 2643 ``` 2644 2645 An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data 2646 delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder 2647 to output to a file. 2648 2649 ```c 2650 ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); 2651 ma_encoder encoder; 2652 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); 2653 if (result != MA_SUCCESS) { 2654 // Error 2655 } 2656 2657 ... 2658 2659 ma_encoder_uninit(&encoder); 2660 ``` 2661 2662 When initializing an encoder you must specify a config which is initialized with 2663 `ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output 2664 channel count and output sample rate. The following file types are supported: 2665 2666 +------------------------+-------------+ 2667 | Enum | Description | 2668 +------------------------+-------------+ 2669 | ma_encoding_format_wav | WAV | 2670 +------------------------+-------------+ 2671 2672 If the format, channel count or sample rate is not supported by the output file type an error will 2673 be returned. The encoder will not perform data conversion so you will need to convert it before 2674 outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the 2675 example below: 2676 2677 ```c 2678 ma_uint64 framesWritten; 2679 result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); 2680 if (result != MA_SUCCESS) { 2681 ... handle error ... 2682 } 2683 ``` 2684 2685 The `framesWritten` variable will contain the number of PCM frames that were actually written. This 2686 is optionally and you can pass in `NULL` if you need this. 2687 2688 Encoders must be uninitialized with `ma_encoder_uninit()`. 2689 2690 2691 2692 10. Data Conversion 2693 =================== 2694 A data conversion API is included with miniaudio which supports the majority of data conversion 2695 requirements. This supports conversion between sample formats, channel counts (with channel 2696 mapping) and sample rates. 2697 2698 2699 10.1. Sample Format Conversion 2700 ------------------------------ 2701 Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and 2702 `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific 2703 formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use 2704 `ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count 2705 and channel count as a variable instead of the total sample count. 2706 2707 2708 10.1.1. Dithering 2709 ----------------- 2710 Dithering can be set using the ditherMode parameter. 2711 2712 The different dithering modes include the following, in order of efficiency: 2713 2714 +-----------+--------------------------+ 2715 | Type | Enum Token | 2716 +-----------+--------------------------+ 2717 | None | ma_dither_mode_none | 2718 | Rectangle | ma_dither_mode_rectangle | 2719 | Triangle | ma_dither_mode_triangle | 2720 +-----------+--------------------------+ 2721 2722 Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be 2723 ignored for conversions where dithering is not needed. Dithering is available for the following 2724 conversions: 2725 2726 ``` 2727 s16 -> u8 2728 s24 -> u8 2729 s32 -> u8 2730 f32 -> u8 2731 s24 -> s16 2732 s32 -> s16 2733 f32 -> s16 2734 ``` 2735 2736 Note that it is not an error to pass something other than ma_dither_mode_none for conversions where 2737 dither is not used. It will just be ignored. 2738 2739 2740 2741 10.2. Channel Conversion 2742 ------------------------ 2743 Channel conversion is used for channel rearrangement and conversion from one channel count to 2744 another. The `ma_channel_converter` API is used for channel conversion. Below is an example of 2745 initializing a simple channel converter which converts from mono to stereo. 2746 2747 ```c 2748 ma_channel_converter_config config = ma_channel_converter_config_init( 2749 ma_format, // Sample format 2750 1, // Input channels 2751 NULL, // Input channel map 2752 2, // Output channels 2753 NULL, // Output channel map 2754 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. 2755 2756 result = ma_channel_converter_init(&config, NULL, &converter); 2757 if (result != MA_SUCCESS) { 2758 // Error. 2759 } 2760 ``` 2761 2762 To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: 2763 2764 ```c 2765 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); 2766 if (result != MA_SUCCESS) { 2767 // Error. 2768 } 2769 ``` 2770 2771 It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM 2772 frames. 2773 2774 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. 2775 2776 2777 10.2.1. Channel Mapping 2778 ----------------------- 2779 In addition to converting from one channel count to another, like the example above, the channel 2780 converter can also be used to rearrange channels. When initializing the channel converter, you can 2781 optionally pass in channel maps for both the input and output frames. If the channel counts are the 2782 same, and each channel map contains the same channel positions with the exception that they're in 2783 a different order, a simple shuffling of the channels will be performed. If, however, there is not 2784 a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed 2785 based on a mixing mode which is specified when initializing the `ma_channel_converter_config` 2786 object. 2787 2788 When converting from mono to multi-channel, the mono channel is simply copied to each output 2789 channel. When going the other way around, the audio of each output channel is simply averaged and 2790 copied to the mono channel. 2791 2792 In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess 2793 channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th 2794 channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 2795 4th channels. 2796 2797 The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a 2798 simple distribution between input and output. Imagine sitting in the middle of a room, with 2799 speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be 2800 thought of as being in the corner of the front and left walls. 2801 2802 Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined 2803 weights. Custom weights can be passed in as the last parameter of 2804 `ma_channel_converter_config_init()`. 2805 2806 Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a 2807 `ma_standard_channel_map` enum as it's first parameter, which can be one of the following: 2808 2809 +-----------------------------------+-----------------------------------------------------------+ 2810 | Name | Description | 2811 +-----------------------------------+-----------------------------------------------------------+ 2812 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | 2813 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | 2814 | ma_standard_channel_map_alsa | Default ALSA channel map. | 2815 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | 2816 | ma_standard_channel_map_flac | FLAC channel map. | 2817 | ma_standard_channel_map_vorbis | Vorbis channel map. | 2818 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | 2819 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | 2820 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | 2821 +-----------------------------------+-----------------------------------------------------------+ 2822 2823 Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): 2824 2825 +---------------+---------------------------------+ 2826 | Channel Count | Mapping | 2827 +---------------+---------------------------------+ 2828 | 1 (Mono) | 0: MA_CHANNEL_MONO | 2829 +---------------+---------------------------------+ 2830 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> | 2831 | | 1: MA_CHANNEL_FRONT_RIGHT | 2832 +---------------+---------------------------------+ 2833 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> | 2834 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2835 | | 2: MA_CHANNEL_FRONT_CENTER | 2836 +---------------+---------------------------------+ 2837 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> | 2838 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2839 | | 2: MA_CHANNEL_FRONT_CENTER <br> | 2840 | | 3: MA_CHANNEL_BACK_CENTER | 2841 +---------------+---------------------------------+ 2842 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> | 2843 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2844 | | 2: MA_CHANNEL_FRONT_CENTER <br> | 2845 | | 3: MA_CHANNEL_BACK_LEFT <br> | 2846 | | 4: MA_CHANNEL_BACK_RIGHT | 2847 +---------------+---------------------------------+ 2848 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> | 2849 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2850 | | 2: MA_CHANNEL_FRONT_CENTER <br> | 2851 | | 3: MA_CHANNEL_LFE <br> | 2852 | | 4: MA_CHANNEL_SIDE_LEFT <br> | 2853 | | 5: MA_CHANNEL_SIDE_RIGHT | 2854 +---------------+---------------------------------+ 2855 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> | 2856 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2857 | | 2: MA_CHANNEL_FRONT_CENTER <br> | 2858 | | 3: MA_CHANNEL_LFE <br> | 2859 | | 4: MA_CHANNEL_BACK_CENTER <br> | 2860 | | 4: MA_CHANNEL_SIDE_LEFT <br> | 2861 | | 5: MA_CHANNEL_SIDE_RIGHT | 2862 +---------------+---------------------------------+ 2863 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> | 2864 | | 1: MA_CHANNEL_FRONT_RIGHT <br> | 2865 | | 2: MA_CHANNEL_FRONT_CENTER <br> | 2866 | | 3: MA_CHANNEL_LFE <br> | 2867 | | 4: MA_CHANNEL_BACK_LEFT <br> | 2868 | | 5: MA_CHANNEL_BACK_RIGHT <br> | 2869 | | 6: MA_CHANNEL_SIDE_LEFT <br> | 2870 | | 7: MA_CHANNEL_SIDE_RIGHT | 2871 +---------------+---------------------------------+ 2872 | Other | All channels set to 0. This | 2873 | | is equivalent to the same | 2874 | | mapping as the device. | 2875 +---------------+---------------------------------+ 2876 2877 2878 2879 10.3. Resampling 2880 ---------------- 2881 Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something 2882 like the following: 2883 2884 ```c 2885 ma_resampler_config config = ma_resampler_config_init( 2886 ma_format_s16, 2887 channels, 2888 sampleRateIn, 2889 sampleRateOut, 2890 ma_resample_algorithm_linear); 2891 2892 ma_resampler resampler; 2893 ma_result result = ma_resampler_init(&config, &resampler); 2894 if (result != MA_SUCCESS) { 2895 // An error occurred... 2896 } 2897 ``` 2898 2899 Do the following to uninitialize the resampler: 2900 2901 ```c 2902 ma_resampler_uninit(&resampler); 2903 ``` 2904 2905 The following example shows how data can be processed 2906 2907 ```c 2908 ma_uint64 frameCountIn = 1000; 2909 ma_uint64 frameCountOut = 2000; 2910 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); 2911 if (result != MA_SUCCESS) { 2912 // An error occurred... 2913 } 2914 2915 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the 2916 // number of output frames written. 2917 ``` 2918 2919 To initialize the resampler you first need to set up a config (`ma_resampler_config`) with 2920 `ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of 2921 channels, the input and output sample rate, and the algorithm. 2922 2923 The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format 2924 you will need to perform pre- and post-conversions yourself where necessary. Note that the format 2925 is the same for both input and output. The format cannot be changed after initialization. 2926 2927 The resampler supports multiple channels and is always interleaved (both input and output). The 2928 channel count cannot be changed after initialization. 2929 2930 The sample rates can be anything other than zero, and are always specified in hertz. They should be 2931 set to something like 44100, etc. The sample rate is the only configuration property that can be 2932 changed after initialization. 2933 2934 The miniaudio resampler has built-in support for the following algorithms: 2935 2936 +-----------+------------------------------+ 2937 | Algorithm | Enum Token | 2938 +-----------+------------------------------+ 2939 | Linear | ma_resample_algorithm_linear | 2940 | Custom | ma_resample_algorithm_custom | 2941 +-----------+------------------------------+ 2942 2943 The algorithm cannot be changed after initialization. 2944 2945 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. 2946 De-interleaved processing is not supported. To process frames, use 2947 `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you 2948 can fit in the output buffer and the number of input frames contained in the input buffer. On 2949 output these variables contain the number of output frames that were written to the output buffer 2950 and the number of input frames that were consumed in the process. You can pass in NULL for the 2951 input buffer in which case it will be treated as an infinitely large buffer of zeros. The output 2952 buffer can also be NULL, in which case the processing will be treated as seek. 2953 2954 The sample rate can be changed dynamically on the fly. You can change this with explicit sample 2955 rates with `ma_resampler_set_rate()` and also with a decimal ratio with 2956 `ma_resampler_set_rate_ratio()`. The ratio is in/out. 2957 2958 Sometimes it's useful to know exactly how many input frames will be required to output a specific 2959 number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. 2960 Likewise, it's sometimes useful to know exactly how many frames would be output given a certain 2961 number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. 2962 2963 Due to the nature of how resampling works, the resampler introduces some latency. This can be 2964 retrieved in terms of both the input rate and the output rate with 2965 `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. 2966 2967 2968 10.3.1. Resampling Algorithms 2969 ----------------------------- 2970 The choice of resampling algorithm depends on your situation and requirements. 2971 2972 2973 10.3.1.1. Linear Resampling 2974 --------------------------- 2975 The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, 2976 some control over the quality of the linear resampler which may make it a suitable option depending 2977 on your requirements. 2978 2979 The linear resampler performs low-pass filtering before or after downsampling or upsampling, 2980 depending on the sample rates you're converting between. When decreasing the sample rate, the 2981 low-pass filter will be applied before downsampling. When increasing the rate it will be performed 2982 after upsampling. By default a fourth order low-pass filter will be applied. This can be configured 2983 via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. 2984 2985 The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of 2986 the input and output sample rates (Nyquist Frequency). 2987 2988 The API for the linear resampler is the same as the main resampler API, only it's called 2989 `ma_linear_resampler`. 2990 2991 2992 10.3.2. Custom Resamplers 2993 ------------------------- 2994 You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling 2995 algorithm and setting a vtable in the resampler config: 2996 2997 ```c 2998 ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); 2999 config.pBackendVTable = &g_customResamplerVTable; 3000 ``` 3001 3002 Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You 3003 need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all 3004 functions in the vtable need to be implemented, but if it's possible to implement, they should be. 3005 3006 You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The 3007 `onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom 3008 resampler will need to make given the supplied config. When you initialize the resampler via the 3009 `onInit` callback, you'll be given a pointer to a heap allocation which is where you should store 3010 the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage 3011 it for you. 3012 3013 The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` 3014 points to a variable containing the number of frames in the `pFramesIn` buffer and 3015 `pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. 3016 On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, 3017 whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. 3018 3019 The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If 3020 dynamic rate changes are not supported, you can set this callback to NULL. 3021 3022 The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in 3023 input and output rates respectively. These can be NULL in which case latency calculations will be 3024 assumed to be NULL. 3025 3026 The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input 3027 frames are required to be available to produce the given number of output frames. Likewise, the 3028 `onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be 3029 produced given the specified number of input frames. miniaudio will use these as a hint, but they 3030 are optional and can be set to NULL if you're unable to implement them. 3031 3032 3033 3034 10.4. General Data Conversion 3035 ----------------------------- 3036 The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and 3037 resampling into one operation. This is what miniaudio uses internally to convert between the format 3038 requested when the device was initialized and the format of the backend's native device. The API 3039 for general data conversion is very similar to the resampling API. Create a `ma_data_converter` 3040 object like this: 3041 3042 ```c 3043 ma_data_converter_config config = ma_data_converter_config_init( 3044 inputFormat, 3045 outputFormat, 3046 inputChannels, 3047 outputChannels, 3048 inputSampleRate, 3049 outputSampleRate 3050 ); 3051 3052 ma_data_converter converter; 3053 ma_result result = ma_data_converter_init(&config, NULL, &converter); 3054 if (result != MA_SUCCESS) { 3055 // An error occurred... 3056 } 3057 ``` 3058 3059 In the example above we use `ma_data_converter_config_init()` to initialize the config, however 3060 there's many more properties that can be configured, such as channel maps and resampling quality. 3061 Something like the following may be more suitable depending on your requirements: 3062 3063 ```c 3064 ma_data_converter_config config = ma_data_converter_config_init_default(); 3065 config.formatIn = inputFormat; 3066 config.formatOut = outputFormat; 3067 config.channelsIn = inputChannels; 3068 config.channelsOut = outputChannels; 3069 config.sampleRateIn = inputSampleRate; 3070 config.sampleRateOut = outputSampleRate; 3071 ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); 3072 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; 3073 ``` 3074 3075 Do the following to uninitialize the data converter: 3076 3077 ```c 3078 ma_data_converter_uninit(&converter, NULL); 3079 ``` 3080 3081 The following example shows how data can be processed 3082 3083 ```c 3084 ma_uint64 frameCountIn = 1000; 3085 ma_uint64 frameCountOut = 2000; 3086 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); 3087 if (result != MA_SUCCESS) { 3088 // An error occurred... 3089 } 3090 3091 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number 3092 // of output frames written. 3093 ``` 3094 3095 The data converter supports multiple channels and is always interleaved (both input and output). 3096 The channel count cannot be changed after initialization. 3097 3098 Sample rates can be anything other than zero, and are always specified in hertz. They should be set 3099 to something like 44100, etc. The sample rate is the only configuration property that can be 3100 changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of 3101 `ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use 3102 `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. 3103 The resampling algorithm cannot be changed after initialization. 3104 3105 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. 3106 De-interleaved processing is not supported. To process frames, use 3107 `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames 3108 you can fit in the output buffer and the number of input frames contained in the input buffer. On 3109 output these variables contain the number of output frames that were written to the output buffer 3110 and the number of input frames that were consumed in the process. You can pass in NULL for the 3111 input buffer in which case it will be treated as an infinitely large 3112 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated 3113 as seek. 3114 3115 Sometimes it's useful to know exactly how many input frames will be required to output a specific 3116 number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. 3117 Likewise, it's sometimes useful to know exactly how many frames would be output given a certain 3118 number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. 3119 3120 Due to the nature of how resampling works, the data converter introduces some latency if resampling 3121 is required. This can be retrieved in terms of both the input rate and the output rate with 3122 `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. 3123 3124 3125 3126 11. Filtering 3127 ============= 3128 3129 11.1. Biquad Filtering 3130 ---------------------- 3131 Biquad filtering is achieved with the `ma_biquad` API. Example: 3132 3133 ```c 3134 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); 3135 ma_result result = ma_biquad_init(&config, &biquad); 3136 if (result != MA_SUCCESS) { 3137 // Error. 3138 } 3139 3140 ... 3141 3142 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); 3143 ``` 3144 3145 Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, 3146 b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and 3147 coefficients must not be pre-normalized. 3148 3149 Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format 3150 you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use 3151 fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. 3152 3153 Input and output frames are always interleaved. 3154 3155 Filtering can be applied in-place by passing in the same pointer for both the input and output 3156 buffers, like so: 3157 3158 ```c 3159 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); 3160 ``` 3161 3162 If you need to change the values of the coefficients, but maintain the values in the registers you 3163 can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the 3164 filter while keeping the values of registers valid to avoid glitching. Do not use 3165 `ma_biquad_init()` for this as it will do a full initialization which involves clearing the 3166 registers to 0. Note that changing the format or channel count after initialization is invalid and 3167 will result in an error. 3168 3169 3170 11.2. Low-Pass Filtering 3171 ------------------------ 3172 Low-pass filtering is achieved with the following APIs: 3173 3174 +---------+------------------------------------------+ 3175 | API | Description | 3176 +---------+------------------------------------------+ 3177 | ma_lpf1 | First order low-pass filter | 3178 | ma_lpf2 | Second order low-pass filter | 3179 | ma_lpf | High order low-pass filter (Butterworth) | 3180 +---------+------------------------------------------+ 3181 3182 Low-pass filter example: 3183 3184 ```c 3185 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); 3186 ma_result result = ma_lpf_init(&config, &lpf); 3187 if (result != MA_SUCCESS) { 3188 // Error. 3189 } 3190 3191 ... 3192 3193 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); 3194 ``` 3195 3196 Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format 3197 you need to convert it yourself beforehand. Input and output frames are always interleaved. 3198 3199 Filtering can be applied in-place by passing in the same pointer for both the input and output 3200 buffers, like so: 3201 3202 ```c 3203 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); 3204 ``` 3205 3206 The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, 3207 you can chain first and second order filters together. 3208 3209 ```c 3210 for (iFilter = 0; iFilter < filterCount; iFilter += 1) { 3211 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); 3212 } 3213 ``` 3214 3215 If you need to change the configuration of the filter, but need to maintain the state of internal 3216 registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample 3217 rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the 3218 format or channel count after initialization is invalid and will result in an error. 3219 3220 The `ma_lpf` object supports a configurable order, but if you only need a first order filter you 3221 may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use 3222 `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. 3223 3224 If an even filter order is specified, a series of second order filters will be processed in a 3225 chain. If an odd filter order is specified, a first order filter will be applied, followed by a 3226 series of second order filters in a chain. 3227 3228 3229 11.3. High-Pass Filtering 3230 ------------------------- 3231 High-pass filtering is achieved with the following APIs: 3232 3233 +---------+-------------------------------------------+ 3234 | API | Description | 3235 +---------+-------------------------------------------+ 3236 | ma_hpf1 | First order high-pass filter | 3237 | ma_hpf2 | Second order high-pass filter | 3238 | ma_hpf | High order high-pass filter (Butterworth) | 3239 +---------+-------------------------------------------+ 3240 3241 High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, 3242 `ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. 3243 3244 3245 11.4. Band-Pass Filtering 3246 ------------------------- 3247 Band-pass filtering is achieved with the following APIs: 3248 3249 +---------+-------------------------------+ 3250 | API | Description | 3251 +---------+-------------------------------+ 3252 | ma_bpf2 | Second order band-pass filter | 3253 | ma_bpf | High order band-pass filter | 3254 +---------+-------------------------------+ 3255 3256 Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and 3257 `ma_hpf`. See example code for low-pass filters for example usage. Note that the order for 3258 band-pass filters must be an even number which means there is no first order band-pass filter, 3259 unlike low-pass and high-pass filters. 3260 3261 3262 11.5. Notch Filtering 3263 --------------------- 3264 Notch filtering is achieved with the following APIs: 3265 3266 +-----------+------------------------------------------+ 3267 | API | Description | 3268 +-----------+------------------------------------------+ 3269 | ma_notch2 | Second order notching filter | 3270 +-----------+------------------------------------------+ 3271 3272 3273 11.6. Peaking EQ Filtering 3274 ------------------------- 3275 Peaking filtering is achieved with the following APIs: 3276 3277 +----------+------------------------------------------+ 3278 | API | Description | 3279 +----------+------------------------------------------+ 3280 | ma_peak2 | Second order peaking filter | 3281 +----------+------------------------------------------+ 3282 3283 3284 11.7. Low Shelf Filtering 3285 ------------------------- 3286 Low shelf filtering is achieved with the following APIs: 3287 3288 +-------------+------------------------------------------+ 3289 | API | Description | 3290 +-------------+------------------------------------------+ 3291 | ma_loshelf2 | Second order low shelf filter | 3292 +-------------+------------------------------------------+ 3293 3294 Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to 3295 just turn them down rather than eliminate them entirely. 3296 3297 3298 11.8. High Shelf Filtering 3299 -------------------------- 3300 High shelf filtering is achieved with the following APIs: 3301 3302 +-------------+------------------------------------------+ 3303 | API | Description | 3304 +-------------+------------------------------------------+ 3305 | ma_hishelf2 | Second order high shelf filter | 3306 +-------------+------------------------------------------+ 3307 3308 The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` 3309 instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, 3310 the high shelf filter does the same thing for high frequencies. 3311 3312 3313 3314 3315 12. Waveform and Noise Generation 3316 ================================= 3317 3318 12.1. Waveforms 3319 --------------- 3320 miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved 3321 with the `ma_waveform` API. Example: 3322 3323 ```c 3324 ma_waveform_config config = ma_waveform_config_init( 3325 FORMAT, 3326 CHANNELS, 3327 SAMPLE_RATE, 3328 ma_waveform_type_sine, 3329 amplitude, 3330 frequency); 3331 3332 ma_waveform waveform; 3333 ma_result result = ma_waveform_init(&config, &waveform); 3334 if (result != MA_SUCCESS) { 3335 // Error. 3336 } 3337 3338 ... 3339 3340 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); 3341 ``` 3342 3343 The amplitude, frequency, type, and sample rate can be changed dynamically with 3344 `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and 3345 `ma_waveform_set_sample_rate()` respectively. 3346 3347 You can invert the waveform by setting the amplitude to a negative value. You can use this to 3348 control whether or not a sawtooth has a positive or negative ramp, for example. 3349 3350 Below are the supported waveform types: 3351 3352 +---------------------------+ 3353 | Enum Name | 3354 +---------------------------+ 3355 | ma_waveform_type_sine | 3356 | ma_waveform_type_square | 3357 | ma_waveform_type_triangle | 3358 | ma_waveform_type_sawtooth | 3359 +---------------------------+ 3360 3361 3362 3363 12.2. Noise 3364 ----------- 3365 miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: 3366 3367 ```c 3368 ma_noise_config config = ma_noise_config_init( 3369 FORMAT, 3370 CHANNELS, 3371 ma_noise_type_white, 3372 SEED, 3373 amplitude); 3374 3375 ma_noise noise; 3376 ma_result result = ma_noise_init(&config, &noise); 3377 if (result != MA_SUCCESS) { 3378 // Error. 3379 } 3380 3381 ... 3382 3383 ma_noise_read_pcm_frames(&noise, pOutput, frameCount); 3384 ``` 3385 3386 The noise API uses simple LCG random number generation. It supports a custom seed which is useful 3387 for things like automated testing requiring reproducibility. Setting the seed to zero will default 3388 to `MA_DEFAULT_LCG_SEED`. 3389 3390 The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and 3391 `ma_noise_set_seed()` respectively. 3392 3393 By default, the noise API will use different values for different channels. So, for example, the 3394 left side in a stereo stream will be different to the right side. To instead have each channel use 3395 the same random value, set the `duplicateChannels` member of the noise config to true, like so: 3396 3397 ```c 3398 config.duplicateChannels = MA_TRUE; 3399 ``` 3400 3401 Below are the supported noise types. 3402 3403 +------------------------+ 3404 | Enum Name | 3405 +------------------------+ 3406 | ma_noise_type_white | 3407 | ma_noise_type_pink | 3408 | ma_noise_type_brownian | 3409 +------------------------+ 3410 3411 3412 3413 13. Audio Buffers 3414 ================= 3415 miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can 3416 read from memory that's managed by the application, but can also handle the memory management for 3417 you internally. Memory management is flexible and should support most use cases. 3418 3419 Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: 3420 3421 ```c 3422 ma_audio_buffer_config config = ma_audio_buffer_config_init( 3423 format, 3424 channels, 3425 sizeInFrames, 3426 pExistingData, 3427 &allocationCallbacks); 3428 3429 ma_audio_buffer buffer; 3430 result = ma_audio_buffer_init(&config, &buffer); 3431 if (result != MA_SUCCESS) { 3432 // Error. 3433 } 3434 3435 ... 3436 3437 ma_audio_buffer_uninit(&buffer); 3438 ``` 3439 3440 In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an 3441 application can do self-managed memory allocation. If you would rather make a copy of the data, use 3442 `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. 3443 3444 Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the 3445 raw audio data in a contiguous block of memory. That is, the raw audio data will be located 3446 immediately after the `ma_audio_buffer` structure. To do this, use 3447 `ma_audio_buffer_alloc_and_init()`: 3448 3449 ```c 3450 ma_audio_buffer_config config = ma_audio_buffer_config_init( 3451 format, 3452 channels, 3453 sizeInFrames, 3454 pExistingData, 3455 &allocationCallbacks); 3456 3457 ma_audio_buffer* pBuffer 3458 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); 3459 if (result != MA_SUCCESS) { 3460 // Error 3461 } 3462 3463 ... 3464 3465 ma_audio_buffer_uninit_and_free(&buffer); 3466 ``` 3467 3468 If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it 3469 with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by 3470 `pExistingData` will be copied into the buffer, which is contrary to the behavior of 3471 `ma_audio_buffer_init()`. 3472 3473 An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the 3474 cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should 3475 loop. The return value is the number of frames actually read. If this is less than the number of 3476 frames requested it means the end has been reached. This should never happen if the `loop` 3477 parameter is set to true. If you want to manually loop back to the start, you can do so with with 3478 `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an 3479 audio buffer. 3480 3481 ```c 3482 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); 3483 if (framesRead < desiredFrameCount) { 3484 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. 3485 } 3486 ``` 3487 3488 Sometimes you may want to avoid the cost of data movement between the internal buffer and the 3489 output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: 3490 3491 ```c 3492 void* pMappedFrames; 3493 ma_uint64 frameCount = frameCountToTryMapping; 3494 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); 3495 if (result == MA_SUCCESS) { 3496 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be 3497 // less due to the end of the buffer being reached. 3498 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); 3499 3500 // You must unmap the buffer. 3501 ma_audio_buffer_unmap(pAudioBuffer, frameCount); 3502 } 3503 ``` 3504 3505 When you use memory mapping, the read cursor is increment by the frame count passed in to 3506 `ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller 3507 than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is 3508 that it does not handle looping for you. You can determine if the buffer is at the end for the 3509 purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of 3510 `ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` 3511 as an error when returned by `ma_audio_buffer_unmap()`. 3512 3513 3514 3515 14. Ring Buffers 3516 ================ 3517 miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via 3518 the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` 3519 operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around 3520 `ma_rb`. 3521 3522 Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved 3523 streams. The caller can also allocate their own backing memory for the ring buffer to use 3524 internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for 3525 you. 3526 3527 The examples below use the PCM frame variant of the ring buffer since that's most likely the one 3528 you will want to use. To initialize a ring buffer, do something like the following: 3529 3530 ```c 3531 ma_pcm_rb rb; 3532 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); 3533 if (result != MA_SUCCESS) { 3534 // Error 3535 } 3536 ``` 3537 3538 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because 3539 it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you 3540 would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes 3541 instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter 3542 is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. 3543 Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. 3544 3545 Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is 3546 offset from each other based on the stride. To manage your sub-buffers you can use 3547 `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and 3548 `ma_pcm_rb_get_subbuffer_ptr()`. 3549 3550 Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section 3551 of the ring buffer. You specify the number of frames you need, and on output it will set to what 3552 was actually acquired. If the read or write pointer is positioned such that the number of frames 3553 requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number 3554 of frames you're given may be less than the number you requested. 3555 3556 After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the 3557 buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is 3558 where the read/write pointers are updated. When you commit you need to pass in the buffer that was 3559 returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is 3560 only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and 3561 `ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was 3562 originally requested. 3563 3564 If you want to correct for drift between the write pointer and the read pointer you can use a 3565 combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and 3566 `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only 3567 move the read pointer forward via the consumer thread, and the write pointer forward by the 3568 producer thread. If there is too much space between the pointers, move the read pointer forward. If 3569 there is too little space between the pointers, move the write pointer forward. 3570 3571 You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` 3572 API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and 3573 instead of frame counts you will pass around byte counts. 3574 3575 The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most 3576 significant bit being used to encode a loop flag and the internally managed buffers always being 3577 aligned to `MA_SIMD_ALIGNMENT`. 3578 3579 Note that the ring buffer is only thread safe when used by a single consumer thread and single 3580 producer thread. 3581 3582 3583 3584 15. Backends 3585 ============ 3586 The following backends are supported by miniaudio. These are listed in order of default priority. 3587 When no backend is specified when initializing a context or device, miniaudio will attempt to use 3588 each of these backends in the order listed in the table below. 3589 3590 Note that backends that are not usable by the build target will not be included in the build. For 3591 example, ALSA, which is specific to Linux, will not be included in the Windows build. 3592 3593 +-------------+-----------------------+--------------------------------------------------------+ 3594 | Name | Enum Name | Supported Operating Systems | 3595 +-------------+-----------------------+--------------------------------------------------------+ 3596 | WASAPI | ma_backend_wasapi | Windows Vista+ | 3597 | DirectSound | ma_backend_dsound | Windows XP+ | 3598 | WinMM | ma_backend_winmm | Windows 95+ | 3599 | Core Audio | ma_backend_coreaudio | macOS, iOS | 3600 | sndio | ma_backend_sndio | OpenBSD | 3601 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | 3602 | OSS | ma_backend_oss | FreeBSD | 3603 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | 3604 | ALSA | ma_backend_alsa | Linux | 3605 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | 3606 | AAudio | ma_backend_aaudio | Android 8+ | 3607 | OpenSL ES | ma_backend_opensl | Android (API level 16+) | 3608 | Web Audio | ma_backend_webaudio | Web (via Emscripten) | 3609 | Custom | ma_backend_custom | Cross Platform | 3610 | Null | ma_backend_null | Cross Platform (not used on Web) | 3611 +-------------+-----------------------+--------------------------------------------------------+ 3612 3613 Some backends have some nuance details you may want to be aware of. 3614 3615 15.1. WASAPI 3616 ------------ 3617 - Low-latency shared mode will be disabled when using an application-defined sample rate which is 3618 different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` 3619 to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing 3620 when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC 3621 will result in miniaudio's internal resampler being used instead which will in turn enable the 3622 use of low-latency shared mode. 3623 3624 15.2. PulseAudio 3625 ---------------- 3626 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: 3627 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. 3628 Alternatively, consider using a different backend such as ALSA. 3629 3630 15.3. Android 3631 ------------- 3632 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: 3633 `<uses-permission android:name="android.permission.RECORD_AUDIO" />` 3634 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a 3635 limitation with OpenSL|ES. 3636 - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration 3637 API (devices are enumerated through Java). You can however perform your own device enumeration 3638 through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it 3639 to ma_device_init(). 3640 - The backend API will perform resampling where possible. The reason for this as opposed to using 3641 miniaudio's built-in resampler is to take advantage of any potential device-specific 3642 optimizations the driver may implement. 3643 3644 BSD 3645 --- 3646 - The sndio backend is currently only enabled on OpenBSD builds. 3647 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can 3648 use it. 3649 3650 15.4. UWP 3651 --------- 3652 - UWP only supports default playback and capture devices. 3653 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): 3654 3655 ``` 3656 <Package ...> 3657 ... 3658 <Capabilities> 3659 <DeviceCapability Name="microphone" /> 3660 </Capabilities> 3661 </Package> 3662 ``` 3663 3664 15.5. Web Audio / Emscripten 3665 ---------------------------- 3666 - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. 3667 - The first time a context is initialized it will create a global object called "miniaudio" whose 3668 primary purpose is to act as a factory for device objects. 3669 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as 3670 they've been deprecated. 3671 - Google has implemented a policy in their browsers that prevent automatic media output without 3672 first receiving some kind of user input. The following web page has additional details: 3673 https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device 3674 may fail if you try to start playback without first handling some kind of user input. 3675 3676 3677 3678 16. Optimization Tips 3679 ===================== 3680 See below for some tips on improving performance. 3681 3682 16.1. Low Level API 3683 ------------------- 3684 - In the data callback, if your data is already clipped prior to copying it into the output buffer, 3685 set the `noClip` config option in the device config to true. This will disable miniaudio's built 3686 in clipping function. 3687 - By default, miniaudio will pre-silence the data callback's output buffer. If you know that you 3688 will always write valid data to the output buffer you can disable pre-silencing by setting the 3689 `noPreSilence` config option in the device config to true. 3690 3691 16.2. High Level API 3692 -------------------- 3693 - If a sound does not require doppler or pitch shifting, consider disabling pitching by 3694 initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. 3695 - If a sound does not require spatialization, disable it by initializing the sound with the 3696 `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with 3697 `ma_sound_set_spatialization_enabled()`. 3698 - If you know all of your sounds will always be the same sample rate, set the engine's sample 3699 rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, 3700 consider setting the decoded sample rate to match your sounds. By configuring everything to 3701 use a consistent sample rate, sample rate conversion can be avoided. 3702 3703 3704 3705 17. Miscellaneous Notes 3706 ======================= 3707 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for 3708 WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though 3709 not all have been tested. 3710 - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This 3711 is due to 64-bit file APIs not being available. 3712 */ 3713 3714 #ifndef miniaudio_h 3715 #define miniaudio_h 3716 3717 #ifdef __cplusplus 3718 extern "C" { 3719 #endif 3720 3721 #define MA_STRINGIFY(x) #x 3722 #define MA_XSTRINGIFY(x) MA_STRINGIFY(x) 3723 3724 #define MA_VERSION_MAJOR 0 3725 #define MA_VERSION_MINOR 11 3726 #define MA_VERSION_REVISION 21 3727 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) 3728 3729 #if defined(_MSC_VER) && !defined(__clang__) 3730 #pragma warning(push) 3731 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ 3732 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ 3733 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ 3734 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 3735 #pragma GCC diagnostic push 3736 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ 3737 #if defined(__clang__) 3738 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ 3739 #endif 3740 #endif 3741 3742 3743 3744 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) 3745 #define MA_SIZEOF_PTR 8 3746 #else 3747 #define MA_SIZEOF_PTR 4 3748 #endif 3749 3750 #include <stddef.h> /* For size_t. */ 3751 3752 /* Sized types. */ 3753 #if defined(MA_USE_STDINT) 3754 #include <stdint.h> 3755 typedef int8_t ma_int8; 3756 typedef uint8_t ma_uint8; 3757 typedef int16_t ma_int16; 3758 typedef uint16_t ma_uint16; 3759 typedef int32_t ma_int32; 3760 typedef uint32_t ma_uint32; 3761 typedef int64_t ma_int64; 3762 typedef uint64_t ma_uint64; 3763 #else 3764 typedef signed char ma_int8; 3765 typedef unsigned char ma_uint8; 3766 typedef signed short ma_int16; 3767 typedef unsigned short ma_uint16; 3768 typedef signed int ma_int32; 3769 typedef unsigned int ma_uint32; 3770 #if defined(_MSC_VER) && !defined(__clang__) 3771 typedef signed __int64 ma_int64; 3772 typedef unsigned __int64 ma_uint64; 3773 #else 3774 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 3775 #pragma GCC diagnostic push 3776 #pragma GCC diagnostic ignored "-Wlong-long" 3777 #if defined(__clang__) 3778 #pragma GCC diagnostic ignored "-Wc++11-long-long" 3779 #endif 3780 #endif 3781 typedef signed long long ma_int64; 3782 typedef unsigned long long ma_uint64; 3783 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 3784 #pragma GCC diagnostic pop 3785 #endif 3786 #endif 3787 #endif /* MA_USE_STDINT */ 3788 3789 #if MA_SIZEOF_PTR == 8 3790 typedef ma_uint64 ma_uintptr; 3791 #else 3792 typedef ma_uint32 ma_uintptr; 3793 #endif 3794 3795 typedef ma_uint8 ma_bool8; 3796 typedef ma_uint32 ma_bool32; 3797 #define MA_TRUE 1 3798 #define MA_FALSE 0 3799 3800 /* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ 3801 typedef float ma_float; 3802 typedef double ma_double; 3803 3804 typedef void* ma_handle; 3805 typedef void* ma_ptr; 3806 3807 /* 3808 ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting 3809 between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get 3810 warning C4191 about "type cast between incompatible function types". To work around this I'm going 3811 to use a different data type depending on the compiler. 3812 */ 3813 #if defined(__GNUC__) 3814 typedef void (*ma_proc)(void); 3815 #else 3816 typedef void* ma_proc; 3817 #endif 3818 3819 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) 3820 typedef ma_uint16 wchar_t; 3821 #endif 3822 3823 /* Define NULL for some compilers. */ 3824 #ifndef NULL 3825 #define NULL 0 3826 #endif 3827 3828 #if defined(SIZE_MAX) 3829 #define MA_SIZE_MAX SIZE_MAX 3830 #else 3831 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ 3832 #endif 3833 3834 3835 /* Platform/backend detection. */ 3836 #if defined(_WIN32) || defined(__COSMOPOLITAN__) 3837 #define MA_WIN32 3838 #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) 3839 #define MA_WIN32_UWP 3840 #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) 3841 #define MA_WIN32_GDK 3842 #else 3843 #define MA_WIN32_DESKTOP 3844 #endif 3845 #endif 3846 #if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ 3847 #define MA_POSIX 3848 3849 /* 3850 Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. 3851 You can use this to avoid including pthread.h in the header section. The downside is that it 3852 results in some fixed sized structures being declared for the various types that are used in 3853 miniaudio. The risk here is that these types might be too small for a given platform. This 3854 risk is yours to take and no support will be offered if you enable this option. 3855 */ 3856 #ifndef MA_NO_PTHREAD_IN_HEADER 3857 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ 3858 typedef pthread_t ma_pthread_t; 3859 typedef pthread_mutex_t ma_pthread_mutex_t; 3860 typedef pthread_cond_t ma_pthread_cond_t; 3861 #else 3862 typedef ma_uintptr ma_pthread_t; 3863 typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; 3864 typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; 3865 #endif 3866 3867 #if defined(__unix__) 3868 #define MA_UNIX 3869 #endif 3870 #if defined(__linux__) 3871 #define MA_LINUX 3872 #endif 3873 #if defined(__APPLE__) 3874 #define MA_APPLE 3875 #endif 3876 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 3877 #define MA_BSD 3878 #endif 3879 #if defined(__ANDROID__) 3880 #define MA_ANDROID 3881 #endif 3882 #if defined(__EMSCRIPTEN__) 3883 #define MA_EMSCRIPTEN 3884 #endif 3885 #if defined(__ORBIS__) 3886 #define MA_ORBIS 3887 #endif 3888 #if defined(__PROSPERO__) 3889 #define MA_PROSPERO 3890 #endif 3891 #if defined(__NX__) 3892 #define MA_NX 3893 #endif 3894 #if defined(__BEOS__) || defined(__HAIKU__) 3895 #define MA_BEOS 3896 #endif 3897 #if defined(__HAIKU__) 3898 #define MA_HAIKU 3899 #endif 3900 #endif 3901 3902 #if defined(__has_c_attribute) 3903 #if __has_c_attribute(fallthrough) 3904 #define MA_FALLTHROUGH [[fallthrough]] 3905 #endif 3906 #endif 3907 #if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) 3908 #if __has_attribute(fallthrough) 3909 #define MA_FALLTHROUGH __attribute__((fallthrough)) 3910 #endif 3911 #endif 3912 #if !defined(MA_FALLTHROUGH) 3913 #define MA_FALLTHROUGH ((void)0) 3914 #endif 3915 3916 #ifdef _MSC_VER 3917 #define MA_INLINE __forceinline 3918 3919 /* noinline was introduced in Visual Studio 2005. */ 3920 #if _MSC_VER >= 1400 3921 #define MA_NO_INLINE __declspec(noinline) 3922 #else 3923 #define MA_NO_INLINE 3924 #endif 3925 #elif defined(__GNUC__) 3926 /* 3927 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when 3928 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some 3929 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the 3930 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue 3931 I am using "__inline__" only when we're compiling in strict ANSI mode. 3932 */ 3933 #if defined(__STRICT_ANSI__) 3934 #define MA_GNUC_INLINE_HINT __inline__ 3935 #else 3936 #define MA_GNUC_INLINE_HINT inline 3937 #endif 3938 3939 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) 3940 #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) 3941 #define MA_NO_INLINE __attribute__((noinline)) 3942 #else 3943 #define MA_INLINE MA_GNUC_INLINE_HINT 3944 #define MA_NO_INLINE __attribute__((noinline)) 3945 #endif 3946 #elif defined(__WATCOMC__) 3947 #define MA_INLINE __inline 3948 #define MA_NO_INLINE 3949 #else 3950 #define MA_INLINE 3951 #define MA_NO_INLINE 3952 #endif 3953 3954 /* MA_DLL is not officially supported. You're on your own if you want to use this. */ 3955 #if defined(MA_DLL) 3956 #if defined(_WIN32) 3957 #define MA_DLL_IMPORT __declspec(dllimport) 3958 #define MA_DLL_EXPORT __declspec(dllexport) 3959 #define MA_DLL_PRIVATE static 3960 #else 3961 #if defined(__GNUC__) && __GNUC__ >= 4 3962 #define MA_DLL_IMPORT __attribute__((visibility("default"))) 3963 #define MA_DLL_EXPORT __attribute__((visibility("default"))) 3964 #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) 3965 #else 3966 #define MA_DLL_IMPORT 3967 #define MA_DLL_EXPORT 3968 #define MA_DLL_PRIVATE static 3969 #endif 3970 #endif 3971 #endif 3972 3973 #if !defined(MA_API) 3974 #if defined(MA_DLL) 3975 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) 3976 #define MA_API MA_DLL_EXPORT 3977 #else 3978 #define MA_API MA_DLL_IMPORT 3979 #endif 3980 #else 3981 #define MA_API extern 3982 #endif 3983 #endif 3984 3985 #if !defined(MA_STATIC) 3986 #if defined(MA_DLL) 3987 #define MA_PRIVATE MA_DLL_PRIVATE 3988 #else 3989 #define MA_PRIVATE static 3990 #endif 3991 #endif 3992 3993 3994 /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ 3995 #define MA_SIMD_ALIGNMENT 32 3996 3997 /* 3998 Special wchar_t type to ensure any structures in the public sections that reference it have a 3999 consistent size across all platforms. 4000 4001 On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use 4002 wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all 4003 platforms. 4004 */ 4005 #if !defined(MA_POSIX) && defined(MA_WIN32) 4006 typedef wchar_t ma_wchar_win32; 4007 #else 4008 typedef ma_uint16 ma_wchar_win32; 4009 #endif 4010 4011 4012 4013 /* 4014 Logging Levels 4015 ============== 4016 Log levels are only used to give logging callbacks some context as to the severity of a log message 4017 so they can do filtering. All log levels will be posted to registered logging callbacks. If you 4018 don't want to output a certain log level you can discriminate against the log level in the callback. 4019 4020 MA_LOG_LEVEL_DEBUG 4021 Used for debugging. Useful for debug and test builds, but should be disabled in release builds. 4022 4023 MA_LOG_LEVEL_INFO 4024 Informational logging. Useful for debugging. This will never be called from within the data 4025 callback. 4026 4027 MA_LOG_LEVEL_WARNING 4028 Warnings. You should enable this in you development builds and action them when encounted. These 4029 logs usually indicate a potential problem or misconfiguration, but still allow you to keep 4030 running. This will never be called from within the data callback. 4031 4032 MA_LOG_LEVEL_ERROR 4033 Error logging. This will be fired when an operation fails and is subsequently aborted. This can 4034 be fired from within the data callback, in which case the device will be stopped. You should 4035 always have this log level enabled. 4036 */ 4037 typedef enum 4038 { 4039 MA_LOG_LEVEL_DEBUG = 4, 4040 MA_LOG_LEVEL_INFO = 3, 4041 MA_LOG_LEVEL_WARNING = 2, 4042 MA_LOG_LEVEL_ERROR = 1 4043 } ma_log_level; 4044 4045 /* 4046 Variables needing to be accessed atomically should be declared with this macro for two reasons: 4047 4048 1) It allows people who read the code to identify a variable as such; and 4049 2) It forces alignment on platforms where it's required or optimal. 4050 4051 Note that for x86/64, alignment is not strictly necessary, but does have some performance 4052 implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU 4053 architecture does not require it, it will simply leave it unaligned. This is the case with old 4054 versions of Visual Studio, which I've confirmed with at least VC6. 4055 */ 4056 #if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) 4057 #include <stdalign.h> 4058 #define MA_ATOMIC(alignment, type) _Alignas(alignment) type 4059 #else 4060 #if defined(__GNUC__) 4061 /* GCC-style compilers. */ 4062 #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) 4063 #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ 4064 /* MSVC. */ 4065 #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type 4066 #else 4067 /* Other compilers. */ 4068 #define MA_ATOMIC(alignment, type) type 4069 #endif 4070 #endif 4071 4072 typedef struct ma_context ma_context; 4073 typedef struct ma_device ma_device; 4074 4075 typedef ma_uint8 ma_channel; 4076 typedef enum 4077 { 4078 MA_CHANNEL_NONE = 0, 4079 MA_CHANNEL_MONO = 1, 4080 MA_CHANNEL_FRONT_LEFT = 2, 4081 MA_CHANNEL_FRONT_RIGHT = 3, 4082 MA_CHANNEL_FRONT_CENTER = 4, 4083 MA_CHANNEL_LFE = 5, 4084 MA_CHANNEL_BACK_LEFT = 6, 4085 MA_CHANNEL_BACK_RIGHT = 7, 4086 MA_CHANNEL_FRONT_LEFT_CENTER = 8, 4087 MA_CHANNEL_FRONT_RIGHT_CENTER = 9, 4088 MA_CHANNEL_BACK_CENTER = 10, 4089 MA_CHANNEL_SIDE_LEFT = 11, 4090 MA_CHANNEL_SIDE_RIGHT = 12, 4091 MA_CHANNEL_TOP_CENTER = 13, 4092 MA_CHANNEL_TOP_FRONT_LEFT = 14, 4093 MA_CHANNEL_TOP_FRONT_CENTER = 15, 4094 MA_CHANNEL_TOP_FRONT_RIGHT = 16, 4095 MA_CHANNEL_TOP_BACK_LEFT = 17, 4096 MA_CHANNEL_TOP_BACK_CENTER = 18, 4097 MA_CHANNEL_TOP_BACK_RIGHT = 19, 4098 MA_CHANNEL_AUX_0 = 20, 4099 MA_CHANNEL_AUX_1 = 21, 4100 MA_CHANNEL_AUX_2 = 22, 4101 MA_CHANNEL_AUX_3 = 23, 4102 MA_CHANNEL_AUX_4 = 24, 4103 MA_CHANNEL_AUX_5 = 25, 4104 MA_CHANNEL_AUX_6 = 26, 4105 MA_CHANNEL_AUX_7 = 27, 4106 MA_CHANNEL_AUX_8 = 28, 4107 MA_CHANNEL_AUX_9 = 29, 4108 MA_CHANNEL_AUX_10 = 30, 4109 MA_CHANNEL_AUX_11 = 31, 4110 MA_CHANNEL_AUX_12 = 32, 4111 MA_CHANNEL_AUX_13 = 33, 4112 MA_CHANNEL_AUX_14 = 34, 4113 MA_CHANNEL_AUX_15 = 35, 4114 MA_CHANNEL_AUX_16 = 36, 4115 MA_CHANNEL_AUX_17 = 37, 4116 MA_CHANNEL_AUX_18 = 38, 4117 MA_CHANNEL_AUX_19 = 39, 4118 MA_CHANNEL_AUX_20 = 40, 4119 MA_CHANNEL_AUX_21 = 41, 4120 MA_CHANNEL_AUX_22 = 42, 4121 MA_CHANNEL_AUX_23 = 43, 4122 MA_CHANNEL_AUX_24 = 44, 4123 MA_CHANNEL_AUX_25 = 45, 4124 MA_CHANNEL_AUX_26 = 46, 4125 MA_CHANNEL_AUX_27 = 47, 4126 MA_CHANNEL_AUX_28 = 48, 4127 MA_CHANNEL_AUX_29 = 49, 4128 MA_CHANNEL_AUX_30 = 50, 4129 MA_CHANNEL_AUX_31 = 51, 4130 MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, 4131 MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, 4132 MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) 4133 } _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ 4134 4135 typedef enum 4136 { 4137 MA_SUCCESS = 0, 4138 MA_ERROR = -1, /* A generic error. */ 4139 MA_INVALID_ARGS = -2, 4140 MA_INVALID_OPERATION = -3, 4141 MA_OUT_OF_MEMORY = -4, 4142 MA_OUT_OF_RANGE = -5, 4143 MA_ACCESS_DENIED = -6, 4144 MA_DOES_NOT_EXIST = -7, 4145 MA_ALREADY_EXISTS = -8, 4146 MA_TOO_MANY_OPEN_FILES = -9, 4147 MA_INVALID_FILE = -10, 4148 MA_TOO_BIG = -11, 4149 MA_PATH_TOO_LONG = -12, 4150 MA_NAME_TOO_LONG = -13, 4151 MA_NOT_DIRECTORY = -14, 4152 MA_IS_DIRECTORY = -15, 4153 MA_DIRECTORY_NOT_EMPTY = -16, 4154 MA_AT_END = -17, 4155 MA_NO_SPACE = -18, 4156 MA_BUSY = -19, 4157 MA_IO_ERROR = -20, 4158 MA_INTERRUPT = -21, 4159 MA_UNAVAILABLE = -22, 4160 MA_ALREADY_IN_USE = -23, 4161 MA_BAD_ADDRESS = -24, 4162 MA_BAD_SEEK = -25, 4163 MA_BAD_PIPE = -26, 4164 MA_DEADLOCK = -27, 4165 MA_TOO_MANY_LINKS = -28, 4166 MA_NOT_IMPLEMENTED = -29, 4167 MA_NO_MESSAGE = -30, 4168 MA_BAD_MESSAGE = -31, 4169 MA_NO_DATA_AVAILABLE = -32, 4170 MA_INVALID_DATA = -33, 4171 MA_TIMEOUT = -34, 4172 MA_NO_NETWORK = -35, 4173 MA_NOT_UNIQUE = -36, 4174 MA_NOT_SOCKET = -37, 4175 MA_NO_ADDRESS = -38, 4176 MA_BAD_PROTOCOL = -39, 4177 MA_PROTOCOL_UNAVAILABLE = -40, 4178 MA_PROTOCOL_NOT_SUPPORTED = -41, 4179 MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, 4180 MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, 4181 MA_SOCKET_NOT_SUPPORTED = -44, 4182 MA_CONNECTION_RESET = -45, 4183 MA_ALREADY_CONNECTED = -46, 4184 MA_NOT_CONNECTED = -47, 4185 MA_CONNECTION_REFUSED = -48, 4186 MA_NO_HOST = -49, 4187 MA_IN_PROGRESS = -50, 4188 MA_CANCELLED = -51, 4189 MA_MEMORY_ALREADY_MAPPED = -52, 4190 4191 /* General non-standard errors. */ 4192 MA_CRC_MISMATCH = -100, 4193 4194 /* General miniaudio-specific errors. */ 4195 MA_FORMAT_NOT_SUPPORTED = -200, 4196 MA_DEVICE_TYPE_NOT_SUPPORTED = -201, 4197 MA_SHARE_MODE_NOT_SUPPORTED = -202, 4198 MA_NO_BACKEND = -203, 4199 MA_NO_DEVICE = -204, 4200 MA_API_NOT_FOUND = -205, 4201 MA_INVALID_DEVICE_CONFIG = -206, 4202 MA_LOOP = -207, 4203 MA_BACKEND_NOT_ENABLED = -208, 4204 4205 /* State errors. */ 4206 MA_DEVICE_NOT_INITIALIZED = -300, 4207 MA_DEVICE_ALREADY_INITIALIZED = -301, 4208 MA_DEVICE_NOT_STARTED = -302, 4209 MA_DEVICE_NOT_STOPPED = -303, 4210 4211 /* Operation errors. */ 4212 MA_FAILED_TO_INIT_BACKEND = -400, 4213 MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, 4214 MA_FAILED_TO_START_BACKEND_DEVICE = -402, 4215 MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 4216 } ma_result; 4217 4218 4219 #define MA_MIN_CHANNELS 1 4220 #ifndef MA_MAX_CHANNELS 4221 #define MA_MAX_CHANNELS 254 4222 #endif 4223 4224 #ifndef MA_MAX_FILTER_ORDER 4225 #define MA_MAX_FILTER_ORDER 8 4226 #endif 4227 4228 typedef enum 4229 { 4230 ma_stream_format_pcm = 0 4231 } ma_stream_format; 4232 4233 typedef enum 4234 { 4235 ma_stream_layout_interleaved = 0, 4236 ma_stream_layout_deinterleaved 4237 } ma_stream_layout; 4238 4239 typedef enum 4240 { 4241 ma_dither_mode_none = 0, 4242 ma_dither_mode_rectangle, 4243 ma_dither_mode_triangle 4244 } ma_dither_mode; 4245 4246 typedef enum 4247 { 4248 /* 4249 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are 4250 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). 4251 */ 4252 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ 4253 ma_format_u8 = 1, 4254 ma_format_s16 = 2, /* Seems to be the most widely supported format. */ 4255 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ 4256 ma_format_s32 = 4, 4257 ma_format_f32 = 5, 4258 ma_format_count 4259 } ma_format; 4260 4261 typedef enum 4262 { 4263 /* Standard rates need to be in priority order. */ 4264 ma_standard_sample_rate_48000 = 48000, /* Most common */ 4265 ma_standard_sample_rate_44100 = 44100, 4266 4267 ma_standard_sample_rate_32000 = 32000, /* Lows */ 4268 ma_standard_sample_rate_24000 = 24000, 4269 ma_standard_sample_rate_22050 = 22050, 4270 4271 ma_standard_sample_rate_88200 = 88200, /* Highs */ 4272 ma_standard_sample_rate_96000 = 96000, 4273 ma_standard_sample_rate_176400 = 176400, 4274 ma_standard_sample_rate_192000 = 192000, 4275 4276 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ 4277 ma_standard_sample_rate_11025 = 11025, 4278 ma_standard_sample_rate_8000 = 8000, 4279 4280 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ 4281 ma_standard_sample_rate_384000 = 384000, 4282 4283 ma_standard_sample_rate_min = ma_standard_sample_rate_8000, 4284 ma_standard_sample_rate_max = ma_standard_sample_rate_384000, 4285 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ 4286 } ma_standard_sample_rate; 4287 4288 4289 typedef enum 4290 { 4291 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ 4292 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ 4293 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ 4294 ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular 4295 } ma_channel_mix_mode; 4296 4297 typedef enum 4298 { 4299 ma_standard_channel_map_microsoft, 4300 ma_standard_channel_map_alsa, 4301 ma_standard_channel_map_rfc3551, /* Based off AIFF. */ 4302 ma_standard_channel_map_flac, 4303 ma_standard_channel_map_vorbis, 4304 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ 4305 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ 4306 ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ 4307 ma_standard_channel_map_default = ma_standard_channel_map_microsoft 4308 } ma_standard_channel_map; 4309 4310 typedef enum 4311 { 4312 ma_performance_profile_low_latency = 0, 4313 ma_performance_profile_conservative 4314 } ma_performance_profile; 4315 4316 4317 typedef struct 4318 { 4319 void* pUserData; 4320 void* (* onMalloc)(size_t sz, void* pUserData); 4321 void* (* onRealloc)(void* p, size_t sz, void* pUserData); 4322 void (* onFree)(void* p, void* pUserData); 4323 } ma_allocation_callbacks; 4324 4325 typedef struct 4326 { 4327 ma_int32 state; 4328 } ma_lcg; 4329 4330 4331 /* 4332 Atomics. 4333 4334 These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too 4335 easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By 4336 using a struct we can enforce the use of atomics at compile time. 4337 4338 These types are declared in the header section because we need to reference them in structs below, but functions for 4339 using them are only exposed in the implementation section. I do not want these to be part of the public API. 4340 4341 There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are 4342 some macros to help with the declarations. They will be named like so: 4343 4344 ma_atomic_uint32 - atomic ma_uint32 4345 ma_atomic_int32 - atomic ma_int32 4346 ma_atomic_uint64 - atomic ma_uint64 4347 ma_atomic_float - atomic float 4348 ma_atomic_bool32 - atomic ma_bool32 4349 4350 The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific 4351 type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: 4352 4353 MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) 4354 4355 Which will declare a type struct that's named like so: 4356 4357 ma_atomic_ptr_node 4358 4359 Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with 4360 the name of the struct. For example: 4361 4362 ma_atomic_uint32_set() - Atomic store of ma_uint32 4363 ma_atomic_uint32_get() - Atomic load of ma_uint32 4364 etc. 4365 4366 For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in 4367 return you get type safety and enforcement of atomic operations. 4368 */ 4369 #define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ 4370 typedef struct \ 4371 { \ 4372 MA_ATOMIC(typeSize, ma_##type) value; \ 4373 } ma_atomic_##type; \ 4374 4375 #define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ 4376 typedef struct \ 4377 { \ 4378 MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ 4379 } ma_atomic_ptr_##type; \ 4380 4381 MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) 4382 MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) 4383 MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) 4384 MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) 4385 MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) 4386 4387 4388 /* Spinlocks are 32-bit for compatibility reasons. */ 4389 typedef ma_uint32 ma_spinlock; 4390 4391 #ifndef MA_NO_THREADING 4392 /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ 4393 typedef enum 4394 { 4395 ma_thread_priority_idle = -5, 4396 ma_thread_priority_lowest = -4, 4397 ma_thread_priority_low = -3, 4398 ma_thread_priority_normal = -2, 4399 ma_thread_priority_high = -1, 4400 ma_thread_priority_highest = 0, 4401 ma_thread_priority_realtime = 1, 4402 ma_thread_priority_default = 0 4403 } ma_thread_priority; 4404 4405 #if defined(MA_POSIX) 4406 typedef ma_pthread_t ma_thread; 4407 #elif defined(MA_WIN32) 4408 typedef ma_handle ma_thread; 4409 #endif 4410 4411 #if defined(MA_POSIX) 4412 typedef ma_pthread_mutex_t ma_mutex; 4413 #elif defined(MA_WIN32) 4414 typedef ma_handle ma_mutex; 4415 #endif 4416 4417 #if defined(MA_POSIX) 4418 typedef struct 4419 { 4420 ma_uint32 value; 4421 ma_pthread_mutex_t lock; 4422 ma_pthread_cond_t cond; 4423 } ma_event; 4424 #elif defined(MA_WIN32) 4425 typedef ma_handle ma_event; 4426 #endif 4427 4428 #if defined(MA_POSIX) 4429 typedef struct 4430 { 4431 int value; 4432 ma_pthread_mutex_t lock; 4433 ma_pthread_cond_t cond; 4434 } ma_semaphore; 4435 #elif defined(MA_WIN32) 4436 typedef ma_handle ma_semaphore; 4437 #endif 4438 #else 4439 /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ 4440 #ifndef MA_NO_DEVICE_IO 4441 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; 4442 #endif 4443 #endif /* MA_NO_THREADING */ 4444 4445 4446 /* 4447 Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. 4448 */ 4449 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); 4450 4451 /* 4452 Retrieves the version of miniaudio as a string which can be useful for logging purposes. 4453 */ 4454 MA_API const char* ma_version_string(void); 4455 4456 4457 /************************************************************************************************************************************************************** 4458 4459 Logging 4460 4461 **************************************************************************************************************************************************************/ 4462 #include <stdarg.h> /* For va_list. */ 4463 4464 #if defined(__has_attribute) 4465 #if __has_attribute(format) 4466 #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) 4467 #endif 4468 #endif 4469 #ifndef MA_ATTRIBUTE_FORMAT 4470 #define MA_ATTRIBUTE_FORMAT(fmt, va) 4471 #endif 4472 4473 #ifndef MA_MAX_LOG_CALLBACKS 4474 #define MA_MAX_LOG_CALLBACKS 4 4475 #endif 4476 4477 4478 /* 4479 The callback for handling log messages. 4480 4481 4482 Parameters 4483 ---------- 4484 pUserData (in) 4485 The user data pointer that was passed into ma_log_register_callback(). 4486 4487 logLevel (in) 4488 The log level. This can be one of the following: 4489 4490 +----------------------+ 4491 | Log Level | 4492 +----------------------+ 4493 | MA_LOG_LEVEL_DEBUG | 4494 | MA_LOG_LEVEL_INFO | 4495 | MA_LOG_LEVEL_WARNING | 4496 | MA_LOG_LEVEL_ERROR | 4497 +----------------------+ 4498 4499 pMessage (in) 4500 The log message. 4501 */ 4502 typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); 4503 4504 typedef struct 4505 { 4506 ma_log_callback_proc onLog; 4507 void* pUserData; 4508 } ma_log_callback; 4509 4510 MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); 4511 4512 4513 typedef struct 4514 { 4515 ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; 4516 ma_uint32 callbackCount; 4517 ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ 4518 #ifndef MA_NO_THREADING 4519 ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ 4520 #endif 4521 } ma_log; 4522 4523 MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); 4524 MA_API void ma_log_uninit(ma_log* pLog); 4525 MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); 4526 MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); 4527 MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); 4528 MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); 4529 MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); 4530 4531 4532 /************************************************************************************************************************************************************** 4533 4534 Biquad Filtering 4535 4536 **************************************************************************************************************************************************************/ 4537 typedef union 4538 { 4539 float f32; 4540 ma_int32 s32; 4541 } ma_biquad_coefficient; 4542 4543 typedef struct 4544 { 4545 ma_format format; 4546 ma_uint32 channels; 4547 double b0; 4548 double b1; 4549 double b2; 4550 double a0; 4551 double a1; 4552 double a2; 4553 } ma_biquad_config; 4554 4555 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); 4556 4557 typedef struct 4558 { 4559 ma_format format; 4560 ma_uint32 channels; 4561 ma_biquad_coefficient b0; 4562 ma_biquad_coefficient b1; 4563 ma_biquad_coefficient b2; 4564 ma_biquad_coefficient a1; 4565 ma_biquad_coefficient a2; 4566 ma_biquad_coefficient* pR1; 4567 ma_biquad_coefficient* pR2; 4568 4569 /* Memory management. */ 4570 void* _pHeap; 4571 ma_bool32 _ownsHeap; 4572 } ma_biquad; 4573 4574 MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); 4575 MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); 4576 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); 4577 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); 4578 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); 4579 MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); 4580 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4581 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); 4582 4583 4584 /************************************************************************************************************************************************************** 4585 4586 Low-Pass Filtering 4587 4588 **************************************************************************************************************************************************************/ 4589 typedef struct 4590 { 4591 ma_format format; 4592 ma_uint32 channels; 4593 ma_uint32 sampleRate; 4594 double cutoffFrequency; 4595 double q; 4596 } ma_lpf1_config, ma_lpf2_config; 4597 4598 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); 4599 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); 4600 4601 typedef struct 4602 { 4603 ma_format format; 4604 ma_uint32 channels; 4605 ma_biquad_coefficient a; 4606 ma_biquad_coefficient* pR1; 4607 4608 /* Memory management. */ 4609 void* _pHeap; 4610 ma_bool32 _ownsHeap; 4611 } ma_lpf1; 4612 4613 MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); 4614 MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); 4615 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); 4616 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); 4617 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); 4618 MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); 4619 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4620 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); 4621 4622 typedef struct 4623 { 4624 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ 4625 } ma_lpf2; 4626 4627 MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); 4628 MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); 4629 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); 4630 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); 4631 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); 4632 MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); 4633 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4634 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); 4635 4636 4637 typedef struct 4638 { 4639 ma_format format; 4640 ma_uint32 channels; 4641 ma_uint32 sampleRate; 4642 double cutoffFrequency; 4643 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ 4644 } ma_lpf_config; 4645 4646 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 4647 4648 typedef struct 4649 { 4650 ma_format format; 4651 ma_uint32 channels; 4652 ma_uint32 sampleRate; 4653 ma_uint32 lpf1Count; 4654 ma_uint32 lpf2Count; 4655 ma_lpf1* pLPF1; 4656 ma_lpf2* pLPF2; 4657 4658 /* Memory management. */ 4659 void* _pHeap; 4660 ma_bool32 _ownsHeap; 4661 } ma_lpf; 4662 4663 MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); 4664 MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); 4665 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); 4666 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); 4667 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); 4668 MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); 4669 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4670 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); 4671 4672 4673 /************************************************************************************************************************************************************** 4674 4675 High-Pass Filtering 4676 4677 **************************************************************************************************************************************************************/ 4678 typedef struct 4679 { 4680 ma_format format; 4681 ma_uint32 channels; 4682 ma_uint32 sampleRate; 4683 double cutoffFrequency; 4684 double q; 4685 } ma_hpf1_config, ma_hpf2_config; 4686 4687 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); 4688 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); 4689 4690 typedef struct 4691 { 4692 ma_format format; 4693 ma_uint32 channels; 4694 ma_biquad_coefficient a; 4695 ma_biquad_coefficient* pR1; 4696 4697 /* Memory management. */ 4698 void* _pHeap; 4699 ma_bool32 _ownsHeap; 4700 } ma_hpf1; 4701 4702 MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); 4703 MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); 4704 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); 4705 MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); 4706 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); 4707 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4708 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); 4709 4710 typedef struct 4711 { 4712 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ 4713 } ma_hpf2; 4714 4715 MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); 4716 MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); 4717 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); 4718 MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); 4719 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); 4720 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4721 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); 4722 4723 4724 typedef struct 4725 { 4726 ma_format format; 4727 ma_uint32 channels; 4728 ma_uint32 sampleRate; 4729 double cutoffFrequency; 4730 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ 4731 } ma_hpf_config; 4732 4733 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 4734 4735 typedef struct 4736 { 4737 ma_format format; 4738 ma_uint32 channels; 4739 ma_uint32 sampleRate; 4740 ma_uint32 hpf1Count; 4741 ma_uint32 hpf2Count; 4742 ma_hpf1* pHPF1; 4743 ma_hpf2* pHPF2; 4744 4745 /* Memory management. */ 4746 void* _pHeap; 4747 ma_bool32 _ownsHeap; 4748 } ma_hpf; 4749 4750 MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); 4751 MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); 4752 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); 4753 MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); 4754 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); 4755 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4756 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); 4757 4758 4759 /************************************************************************************************************************************************************** 4760 4761 Band-Pass Filtering 4762 4763 **************************************************************************************************************************************************************/ 4764 typedef struct 4765 { 4766 ma_format format; 4767 ma_uint32 channels; 4768 ma_uint32 sampleRate; 4769 double cutoffFrequency; 4770 double q; 4771 } ma_bpf2_config; 4772 4773 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); 4774 4775 typedef struct 4776 { 4777 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ 4778 } ma_bpf2; 4779 4780 MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); 4781 MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); 4782 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); 4783 MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); 4784 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); 4785 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4786 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); 4787 4788 4789 typedef struct 4790 { 4791 ma_format format; 4792 ma_uint32 channels; 4793 ma_uint32 sampleRate; 4794 double cutoffFrequency; 4795 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ 4796 } ma_bpf_config; 4797 4798 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 4799 4800 typedef struct 4801 { 4802 ma_format format; 4803 ma_uint32 channels; 4804 ma_uint32 bpf2Count; 4805 ma_bpf2* pBPF2; 4806 4807 /* Memory management. */ 4808 void* _pHeap; 4809 ma_bool32 _ownsHeap; 4810 } ma_bpf; 4811 4812 MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); 4813 MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); 4814 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); 4815 MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); 4816 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); 4817 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4818 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); 4819 4820 4821 /************************************************************************************************************************************************************** 4822 4823 Notching Filter 4824 4825 **************************************************************************************************************************************************************/ 4826 typedef struct 4827 { 4828 ma_format format; 4829 ma_uint32 channels; 4830 ma_uint32 sampleRate; 4831 double q; 4832 double frequency; 4833 } ma_notch2_config, ma_notch_config; 4834 4835 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); 4836 4837 typedef struct 4838 { 4839 ma_biquad bq; 4840 } ma_notch2; 4841 4842 MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); 4843 MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); 4844 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); 4845 MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); 4846 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); 4847 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4848 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); 4849 4850 4851 /************************************************************************************************************************************************************** 4852 4853 Peaking EQ Filter 4854 4855 **************************************************************************************************************************************************************/ 4856 typedef struct 4857 { 4858 ma_format format; 4859 ma_uint32 channels; 4860 ma_uint32 sampleRate; 4861 double gainDB; 4862 double q; 4863 double frequency; 4864 } ma_peak2_config, ma_peak_config; 4865 4866 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); 4867 4868 typedef struct 4869 { 4870 ma_biquad bq; 4871 } ma_peak2; 4872 4873 MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); 4874 MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); 4875 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); 4876 MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); 4877 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); 4878 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4879 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); 4880 4881 4882 /************************************************************************************************************************************************************** 4883 4884 Low Shelf Filter 4885 4886 **************************************************************************************************************************************************************/ 4887 typedef struct 4888 { 4889 ma_format format; 4890 ma_uint32 channels; 4891 ma_uint32 sampleRate; 4892 double gainDB; 4893 double shelfSlope; 4894 double frequency; 4895 } ma_loshelf2_config, ma_loshelf_config; 4896 4897 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); 4898 4899 typedef struct 4900 { 4901 ma_biquad bq; 4902 } ma_loshelf2; 4903 4904 MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); 4905 MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); 4906 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); 4907 MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); 4908 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); 4909 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4910 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); 4911 4912 4913 /************************************************************************************************************************************************************** 4914 4915 High Shelf Filter 4916 4917 **************************************************************************************************************************************************************/ 4918 typedef struct 4919 { 4920 ma_format format; 4921 ma_uint32 channels; 4922 ma_uint32 sampleRate; 4923 double gainDB; 4924 double shelfSlope; 4925 double frequency; 4926 } ma_hishelf2_config, ma_hishelf_config; 4927 4928 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); 4929 4930 typedef struct 4931 { 4932 ma_biquad bq; 4933 } ma_hishelf2; 4934 4935 MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); 4936 MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); 4937 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); 4938 MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); 4939 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); 4940 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 4941 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); 4942 4943 4944 4945 /* 4946 Delay 4947 */ 4948 typedef struct 4949 { 4950 ma_uint32 channels; 4951 ma_uint32 sampleRate; 4952 ma_uint32 delayInFrames; 4953 ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ 4954 float wet; /* 0..1. Default = 1. */ 4955 float dry; /* 0..1. Default = 1. */ 4956 float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ 4957 } ma_delay_config; 4958 4959 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); 4960 4961 4962 typedef struct 4963 { 4964 ma_delay_config config; 4965 ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ 4966 ma_uint32 bufferSizeInFrames; 4967 float* pBuffer; 4968 } ma_delay; 4969 4970 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); 4971 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); 4972 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); 4973 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); 4974 MA_API float ma_delay_get_wet(const ma_delay* pDelay); 4975 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); 4976 MA_API float ma_delay_get_dry(const ma_delay* pDelay); 4977 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); 4978 MA_API float ma_delay_get_decay(const ma_delay* pDelay); 4979 4980 4981 /* Gainer for smooth volume changes. */ 4982 typedef struct 4983 { 4984 ma_uint32 channels; 4985 ma_uint32 smoothTimeInFrames; 4986 } ma_gainer_config; 4987 4988 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); 4989 4990 4991 typedef struct 4992 { 4993 ma_gainer_config config; 4994 ma_uint32 t; 4995 float masterVolume; 4996 float* pOldGains; 4997 float* pNewGains; 4998 4999 /* Memory management. */ 5000 void* _pHeap; 5001 ma_bool32 _ownsHeap; 5002 } ma_gainer; 5003 5004 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); 5005 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); 5006 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); 5007 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); 5008 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 5009 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); 5010 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); 5011 MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); 5012 MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); 5013 5014 5015 5016 /* Stereo panner. */ 5017 typedef enum 5018 { 5019 ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ 5020 ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ 5021 } ma_pan_mode; 5022 5023 typedef struct 5024 { 5025 ma_format format; 5026 ma_uint32 channels; 5027 ma_pan_mode mode; 5028 float pan; 5029 } ma_panner_config; 5030 5031 MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); 5032 5033 5034 typedef struct 5035 { 5036 ma_format format; 5037 ma_uint32 channels; 5038 ma_pan_mode mode; 5039 float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ 5040 } ma_panner; 5041 5042 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); 5043 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 5044 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); 5045 MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); 5046 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); 5047 MA_API float ma_panner_get_pan(const ma_panner* pPanner); 5048 5049 5050 5051 /* Fader. */ 5052 typedef struct 5053 { 5054 ma_format format; 5055 ma_uint32 channels; 5056 ma_uint32 sampleRate; 5057 } ma_fader_config; 5058 5059 MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); 5060 5061 typedef struct 5062 { 5063 ma_fader_config config; 5064 float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ 5065 float volumeEnd; 5066 ma_uint64 lengthInFrames; /* The total length of the fade. */ 5067 ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ 5068 } ma_fader; 5069 5070 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); 5071 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 5072 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); 5073 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); 5074 MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); 5075 MA_API float ma_fader_get_current_volume(const ma_fader* pFader); 5076 5077 5078 5079 /* Spatializer. */ 5080 typedef struct 5081 { 5082 float x; 5083 float y; 5084 float z; 5085 } ma_vec3f; 5086 5087 typedef struct 5088 { 5089 ma_vec3f v; 5090 ma_spinlock lock; 5091 } ma_atomic_vec3f; 5092 5093 typedef enum 5094 { 5095 ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ 5096 ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ 5097 ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ 5098 ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ 5099 } ma_attenuation_model; 5100 5101 typedef enum 5102 { 5103 ma_positioning_absolute, 5104 ma_positioning_relative 5105 } ma_positioning; 5106 5107 typedef enum 5108 { 5109 ma_handedness_right, 5110 ma_handedness_left 5111 } ma_handedness; 5112 5113 5114 typedef struct 5115 { 5116 ma_uint32 channelsOut; 5117 ma_channel* pChannelMapOut; 5118 ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ 5119 float coneInnerAngleInRadians; 5120 float coneOuterAngleInRadians; 5121 float coneOuterGain; 5122 float speedOfSound; 5123 ma_vec3f worldUp; 5124 } ma_spatializer_listener_config; 5125 5126 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); 5127 5128 5129 typedef struct 5130 { 5131 ma_spatializer_listener_config config; 5132 ma_atomic_vec3f position; /* The absolute position of the listener. */ 5133 ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ 5134 ma_atomic_vec3f velocity; 5135 ma_bool32 isEnabled; 5136 5137 /* Memory management. */ 5138 ma_bool32 _ownsHeap; 5139 void* _pHeap; 5140 } ma_spatializer_listener; 5141 5142 MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); 5143 MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); 5144 MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); 5145 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); 5146 MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); 5147 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); 5148 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); 5149 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); 5150 MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); 5151 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); 5152 MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); 5153 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); 5154 MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); 5155 MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); 5156 MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); 5157 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); 5158 MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); 5159 MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); 5160 MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); 5161 5162 5163 typedef struct 5164 { 5165 ma_uint32 channelsIn; 5166 ma_uint32 channelsOut; 5167 ma_channel* pChannelMapIn; 5168 ma_attenuation_model attenuationModel; 5169 ma_positioning positioning; 5170 ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ 5171 float minGain; 5172 float maxGain; 5173 float minDistance; 5174 float maxDistance; 5175 float rolloff; 5176 float coneInnerAngleInRadians; 5177 float coneOuterAngleInRadians; 5178 float coneOuterGain; 5179 float dopplerFactor; /* Set to 0 to disable doppler effect. */ 5180 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ 5181 float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ 5182 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ 5183 } ma_spatializer_config; 5184 5185 MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); 5186 5187 5188 typedef struct 5189 { 5190 ma_uint32 channelsIn; 5191 ma_uint32 channelsOut; 5192 ma_channel* pChannelMapIn; 5193 ma_attenuation_model attenuationModel; 5194 ma_positioning positioning; 5195 ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ 5196 float minGain; 5197 float maxGain; 5198 float minDistance; 5199 float maxDistance; 5200 float rolloff; 5201 float coneInnerAngleInRadians; 5202 float coneOuterAngleInRadians; 5203 float coneOuterGain; 5204 float dopplerFactor; /* Set to 0 to disable doppler effect. */ 5205 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ 5206 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ 5207 ma_atomic_vec3f position; 5208 ma_atomic_vec3f direction; 5209 ma_atomic_vec3f velocity; /* For doppler effect. */ 5210 float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ 5211 float minSpatializationChannelGain; 5212 ma_gainer gainer; /* For smooth gain transitions. */ 5213 float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ 5214 5215 /* Memory management. */ 5216 void* _pHeap; 5217 ma_bool32 _ownsHeap; 5218 } ma_spatializer; 5219 5220 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); 5221 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); 5222 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); 5223 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); 5224 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 5225 MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); 5226 MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); 5227 MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); 5228 MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); 5229 MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); 5230 MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); 5231 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); 5232 MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); 5233 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); 5234 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); 5235 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); 5236 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); 5237 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); 5238 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); 5239 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); 5240 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); 5241 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); 5242 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); 5243 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); 5244 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); 5245 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); 5246 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); 5247 MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); 5248 MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); 5249 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); 5250 MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); 5251 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); 5252 MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); 5253 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); 5254 MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); 5255 MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); 5256 5257 5258 5259 /************************************************************************************************************************************************************ 5260 ************************************************************************************************************************************************************* 5261 5262 DATA CONVERSION 5263 =============== 5264 5265 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. 5266 5267 ************************************************************************************************************************************************************* 5268 ************************************************************************************************************************************************************/ 5269 5270 /************************************************************************************************************************************************************** 5271 5272 Resampling 5273 5274 **************************************************************************************************************************************************************/ 5275 typedef struct 5276 { 5277 ma_format format; 5278 ma_uint32 channels; 5279 ma_uint32 sampleRateIn; 5280 ma_uint32 sampleRateOut; 5281 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ 5282 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ 5283 } ma_linear_resampler_config; 5284 5285 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); 5286 5287 typedef struct 5288 { 5289 ma_linear_resampler_config config; 5290 ma_uint32 inAdvanceInt; 5291 ma_uint32 inAdvanceFrac; 5292 ma_uint32 inTimeInt; 5293 ma_uint32 inTimeFrac; 5294 union 5295 { 5296 float* f32; 5297 ma_int16* s16; 5298 } x0; /* The previous input frame. */ 5299 union 5300 { 5301 float* f32; 5302 ma_int16* s16; 5303 } x1; /* The next input frame. */ 5304 ma_lpf lpf; 5305 5306 /* Memory management. */ 5307 void* _pHeap; 5308 ma_bool32 _ownsHeap; 5309 } ma_linear_resampler; 5310 5311 MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); 5312 MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); 5313 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); 5314 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); 5315 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); 5316 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); 5317 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); 5318 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); 5319 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); 5320 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); 5321 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); 5322 MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); 5323 5324 5325 typedef struct ma_resampler_config ma_resampler_config; 5326 5327 typedef void ma_resampling_backend; 5328 typedef struct 5329 { 5330 ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); 5331 ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); 5332 void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); 5333 ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); 5334 ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ 5335 ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ 5336 ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ 5337 ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ 5338 ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ 5339 ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); 5340 } ma_resampling_backend_vtable; 5341 5342 typedef enum 5343 { 5344 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ 5345 ma_resample_algorithm_custom, 5346 } ma_resample_algorithm; 5347 5348 struct ma_resampler_config 5349 { 5350 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ 5351 ma_uint32 channels; 5352 ma_uint32 sampleRateIn; 5353 ma_uint32 sampleRateOut; 5354 ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ 5355 ma_resampling_backend_vtable* pBackendVTable; 5356 void* pBackendUserData; 5357 struct 5358 { 5359 ma_uint32 lpfOrder; 5360 } linear; 5361 }; 5362 5363 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); 5364 5365 typedef struct 5366 { 5367 ma_resampling_backend* pBackend; 5368 ma_resampling_backend_vtable* pBackendVTable; 5369 void* pBackendUserData; 5370 ma_format format; 5371 ma_uint32 channels; 5372 ma_uint32 sampleRateIn; 5373 ma_uint32 sampleRateOut; 5374 union 5375 { 5376 ma_linear_resampler linear; 5377 } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ 5378 5379 /* Memory management. */ 5380 void* _pHeap; 5381 ma_bool32 _ownsHeap; 5382 } ma_resampler; 5383 5384 MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); 5385 MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); 5386 5387 /* 5388 Initializes a new resampler object from a config. 5389 */ 5390 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); 5391 5392 /* 5393 Uninitializes a resampler. 5394 */ 5395 MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); 5396 5397 /* 5398 Converts the given input data. 5399 5400 Both the input and output frames must be in the format specified in the config when the resampler was initialized. 5401 5402 On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that 5403 were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use 5404 ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. 5405 5406 On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole 5407 input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames 5408 you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. 5409 5410 If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of 5411 output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input 5412 frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be 5413 processed. In this case, any internal filter state will be updated as if zeroes were passed in. 5414 5415 It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. 5416 5417 It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. 5418 */ 5419 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); 5420 5421 5422 /* 5423 Sets the input and output sample rate. 5424 */ 5425 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); 5426 5427 /* 5428 Sets the input and output sample rate as a ratio. 5429 5430 The ration is in/out. 5431 */ 5432 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); 5433 5434 /* 5435 Retrieves the latency introduced by the resampler in input frames. 5436 */ 5437 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); 5438 5439 /* 5440 Retrieves the latency introduced by the resampler in output frames. 5441 */ 5442 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); 5443 5444 /* 5445 Calculates the number of whole input frames that would need to be read from the client in order to output the specified 5446 number of output frames. 5447 5448 The returned value does not include cached input frames. It only returns the number of extra frames that would need to be 5449 read from the input buffer in order to output the specified number of output frames. 5450 */ 5451 MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); 5452 5453 /* 5454 Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of 5455 input frames. 5456 */ 5457 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); 5458 5459 /* 5460 Resets the resampler's timer and clears it's internal cache. 5461 */ 5462 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); 5463 5464 5465 /************************************************************************************************************************************************************** 5466 5467 Channel Conversion 5468 5469 **************************************************************************************************************************************************************/ 5470 typedef enum 5471 { 5472 ma_channel_conversion_path_unknown, 5473 ma_channel_conversion_path_passthrough, 5474 ma_channel_conversion_path_mono_out, /* Converting to mono. */ 5475 ma_channel_conversion_path_mono_in, /* Converting from mono. */ 5476 ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ 5477 ma_channel_conversion_path_weights /* Blended based on weights. */ 5478 } ma_channel_conversion_path; 5479 5480 typedef enum 5481 { 5482 ma_mono_expansion_mode_duplicate = 0, /* The default. */ 5483 ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ 5484 ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ 5485 ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate 5486 } ma_mono_expansion_mode; 5487 5488 typedef struct 5489 { 5490 ma_format format; 5491 ma_uint32 channelsIn; 5492 ma_uint32 channelsOut; 5493 const ma_channel* pChannelMapIn; 5494 const ma_channel* pChannelMapOut; 5495 ma_channel_mix_mode mixingMode; 5496 ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ 5497 float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ 5498 } ma_channel_converter_config; 5499 5500 MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); 5501 5502 typedef struct 5503 { 5504 ma_format format; 5505 ma_uint32 channelsIn; 5506 ma_uint32 channelsOut; 5507 ma_channel_mix_mode mixingMode; 5508 ma_channel_conversion_path conversionPath; 5509 ma_channel* pChannelMapIn; 5510 ma_channel* pChannelMapOut; 5511 ma_uint8* pShuffleTable; /* Indexed by output channel index. */ 5512 union 5513 { 5514 float** f32; 5515 ma_int32** s16; 5516 } weights; /* [in][out] */ 5517 5518 /* Memory management. */ 5519 void* _pHeap; 5520 ma_bool32 _ownsHeap; 5521 } ma_channel_converter; 5522 5523 MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); 5524 MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); 5525 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); 5526 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); 5527 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); 5528 MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); 5529 MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); 5530 5531 5532 /************************************************************************************************************************************************************** 5533 5534 Data Conversion 5535 5536 **************************************************************************************************************************************************************/ 5537 typedef struct 5538 { 5539 ma_format formatIn; 5540 ma_format formatOut; 5541 ma_uint32 channelsIn; 5542 ma_uint32 channelsOut; 5543 ma_uint32 sampleRateIn; 5544 ma_uint32 sampleRateOut; 5545 ma_channel* pChannelMapIn; 5546 ma_channel* pChannelMapOut; 5547 ma_dither_mode ditherMode; 5548 ma_channel_mix_mode channelMixMode; 5549 ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ 5550 float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ 5551 ma_bool32 allowDynamicSampleRate; 5552 ma_resampler_config resampling; 5553 } ma_data_converter_config; 5554 5555 MA_API ma_data_converter_config ma_data_converter_config_init_default(void); 5556 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); 5557 5558 5559 typedef enum 5560 { 5561 ma_data_converter_execution_path_passthrough, /* No conversion. */ 5562 ma_data_converter_execution_path_format_only, /* Only format conversion. */ 5563 ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ 5564 ma_data_converter_execution_path_resample_only, /* Only resampling. */ 5565 ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ 5566 ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ 5567 } ma_data_converter_execution_path; 5568 5569 typedef struct 5570 { 5571 ma_format formatIn; 5572 ma_format formatOut; 5573 ma_uint32 channelsIn; 5574 ma_uint32 channelsOut; 5575 ma_uint32 sampleRateIn; 5576 ma_uint32 sampleRateOut; 5577 ma_dither_mode ditherMode; 5578 ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ 5579 ma_channel_converter channelConverter; 5580 ma_resampler resampler; 5581 ma_bool8 hasPreFormatConversion; 5582 ma_bool8 hasPostFormatConversion; 5583 ma_bool8 hasChannelConverter; 5584 ma_bool8 hasResampler; 5585 ma_bool8 isPassthrough; 5586 5587 /* Memory management. */ 5588 ma_bool8 _ownsHeap; 5589 void* _pHeap; 5590 } ma_data_converter; 5591 5592 MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); 5593 MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); 5594 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); 5595 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); 5596 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); 5597 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); 5598 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); 5599 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); 5600 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); 5601 MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); 5602 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); 5603 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); 5604 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); 5605 MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); 5606 5607 5608 /************************************************************************************************************************************************************ 5609 5610 Format Conversion 5611 5612 ************************************************************************************************************************************************************/ 5613 MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5614 MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5615 MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5616 MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5617 MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5618 MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5619 MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5620 MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5621 MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5622 MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5623 MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5624 MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5625 MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5626 MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5627 MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5628 MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5629 MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5630 MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5631 MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5632 MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); 5633 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); 5634 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); 5635 5636 /* 5637 Deinterleaves an interleaved buffer. 5638 */ 5639 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); 5640 5641 /* 5642 Interleaves a group of deinterleaved buffers. 5643 */ 5644 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); 5645 5646 5647 /************************************************************************************************************************************************************ 5648 5649 Channel Maps 5650 5651 ************************************************************************************************************************************************************/ 5652 /* 5653 This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. 5654 */ 5655 #define MA_CHANNEL_INDEX_NULL 255 5656 5657 /* 5658 Retrieves the channel position of the specified channel in the given channel map. 5659 5660 The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. 5661 */ 5662 MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); 5663 5664 /* 5665 Initializes a blank channel map. 5666 5667 When a blank channel map is specified anywhere it indicates that the native channel map should be used. 5668 */ 5669 MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); 5670 5671 /* 5672 Helper for retrieving a standard channel map. 5673 5674 The output channel map buffer must have a capacity of at least `channelMapCap`. 5675 */ 5676 MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); 5677 5678 /* 5679 Copies a channel map. 5680 5681 Both input and output channel map buffers must have a capacity of at at least `channels`. 5682 */ 5683 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); 5684 5685 /* 5686 Copies a channel map if one is specified, otherwise copies the default channel map. 5687 5688 The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. 5689 */ 5690 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); 5691 5692 5693 /* 5694 Determines whether or not a channel map is valid. 5695 5696 A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but 5697 is usually treated as a passthrough. 5698 5699 Invalid channel maps: 5700 - A channel map with no channels 5701 - A channel map with more than one channel and a mono channel 5702 5703 The channel map buffer must have a capacity of at least `channels`. 5704 */ 5705 MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); 5706 5707 /* 5708 Helper for comparing two channel maps for equality. 5709 5710 This assumes the channel count is the same between the two. 5711 5712 Both channels map buffers must have a capacity of at least `channels`. 5713 */ 5714 MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); 5715 5716 /* 5717 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). 5718 5719 The channel map buffer must have a capacity of at least `channels`. 5720 */ 5721 MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); 5722 5723 /* 5724 Helper for determining whether or not a channel is present in the given channel map. 5725 5726 The channel map buffer must have a capacity of at least `channels`. 5727 */ 5728 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); 5729 5730 /* 5731 Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The 5732 index of the channel is output to `pChannelIndex`. 5733 5734 The channel map buffer must have a capacity of at least `channels`. 5735 */ 5736 MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); 5737 5738 /* 5739 Generates a string representing the given channel map. 5740 5741 This is for printing and debugging purposes, not serialization/deserialization. 5742 5743 Returns the length of the string, not including the null terminator. 5744 */ 5745 MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); 5746 5747 /* 5748 Retrieves a human readable version of a channel position. 5749 */ 5750 MA_API const char* ma_channel_position_to_string(ma_channel channel); 5751 5752 5753 /************************************************************************************************************************************************************ 5754 5755 Conversion Helpers 5756 5757 ************************************************************************************************************************************************************/ 5758 5759 /* 5760 High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to 5761 determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is 5762 ignored. 5763 5764 A return value of 0 indicates an error. 5765 5766 This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. 5767 */ 5768 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); 5769 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); 5770 5771 5772 /************************************************************************************************************************************************************ 5773 5774 Data Source 5775 5776 ************************************************************************************************************************************************************/ 5777 typedef void ma_data_source; 5778 5779 #define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 5780 5781 typedef struct 5782 { 5783 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 5784 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); 5785 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 5786 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); 5787 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); 5788 ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); 5789 ma_uint32 flags; 5790 } ma_data_source_vtable; 5791 5792 typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); 5793 5794 typedef struct 5795 { 5796 const ma_data_source_vtable* vtable; 5797 } ma_data_source_config; 5798 5799 MA_API ma_data_source_config ma_data_source_config_init(void); 5800 5801 5802 typedef struct 5803 { 5804 const ma_data_source_vtable* vtable; 5805 ma_uint64 rangeBegInFrames; 5806 ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ 5807 ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ 5808 ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ 5809 ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ 5810 ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ 5811 ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ 5812 MA_ATOMIC(4, ma_bool32) isLooping; 5813 } ma_data_source_base; 5814 5815 MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); 5816 MA_API void ma_data_source_uninit(ma_data_source* pDataSource); 5817 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ 5818 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ 5819 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); 5820 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 5821 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); 5822 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ 5823 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); 5824 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); 5825 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); 5826 MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); 5827 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); 5828 MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); 5829 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); 5830 MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); 5831 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); 5832 MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); 5833 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); 5834 MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); 5835 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); 5836 MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); 5837 5838 5839 typedef struct 5840 { 5841 ma_data_source_base ds; 5842 ma_format format; 5843 ma_uint32 channels; 5844 ma_uint32 sampleRate; 5845 ma_uint64 cursor; 5846 ma_uint64 sizeInFrames; 5847 const void* pData; 5848 } ma_audio_buffer_ref; 5849 5850 MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); 5851 MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); 5852 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); 5853 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); 5854 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); 5855 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); 5856 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ 5857 MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); 5858 MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); 5859 MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); 5860 MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); 5861 5862 5863 5864 typedef struct 5865 { 5866 ma_format format; 5867 ma_uint32 channels; 5868 ma_uint32 sampleRate; 5869 ma_uint64 sizeInFrames; 5870 const void* pData; /* If set to NULL, will allocate a block of memory for you. */ 5871 ma_allocation_callbacks allocationCallbacks; 5872 } ma_audio_buffer_config; 5873 5874 MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); 5875 5876 typedef struct 5877 { 5878 ma_audio_buffer_ref ref; 5879 ma_allocation_callbacks allocationCallbacks; 5880 ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ 5881 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ 5882 } ma_audio_buffer; 5883 5884 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); 5885 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); 5886 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ 5887 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); 5888 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); 5889 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); 5890 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); 5891 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); 5892 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ 5893 MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); 5894 MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); 5895 MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); 5896 MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); 5897 5898 5899 /* 5900 Paged Audio Buffer 5901 ================== 5902 A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It 5903 can be used for cases where audio data is streamed in asynchronously while allowing data to be read 5904 at the same time. 5905 5906 This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across 5907 simultaneously across different threads, however only one thread at a time can append, and only one 5908 thread at a time can read and seek. 5909 */ 5910 typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; 5911 struct ma_paged_audio_buffer_page 5912 { 5913 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; 5914 ma_uint64 sizeInFrames; 5915 ma_uint8 pAudioData[1]; 5916 }; 5917 5918 typedef struct 5919 { 5920 ma_format format; 5921 ma_uint32 channels; 5922 ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ 5923 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ 5924 } ma_paged_audio_buffer_data; 5925 5926 MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); 5927 MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); 5928 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); 5929 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); 5930 MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); 5931 MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); 5932 MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); 5933 MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); 5934 MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); 5935 5936 5937 typedef struct 5938 { 5939 ma_paged_audio_buffer_data* pData; /* Must not be null. */ 5940 } ma_paged_audio_buffer_config; 5941 5942 MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); 5943 5944 5945 typedef struct 5946 { 5947 ma_data_source_base ds; 5948 ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ 5949 ma_paged_audio_buffer_page* pCurrent; 5950 ma_uint64 relativeCursor; /* Relative to the current page. */ 5951 ma_uint64 absoluteCursor; 5952 } ma_paged_audio_buffer; 5953 5954 MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); 5955 MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); 5956 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ 5957 MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); 5958 MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); 5959 MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); 5960 5961 5962 5963 /************************************************************************************************************************************************************ 5964 5965 Ring Buffer 5966 5967 ************************************************************************************************************************************************************/ 5968 typedef struct 5969 { 5970 void* pBuffer; 5971 ma_uint32 subbufferSizeInBytes; 5972 ma_uint32 subbufferCount; 5973 ma_uint32 subbufferStrideInBytes; 5974 MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ 5975 MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ 5976 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ 5977 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ 5978 ma_allocation_callbacks allocationCallbacks; 5979 } ma_rb; 5980 5981 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); 5982 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); 5983 MA_API void ma_rb_uninit(ma_rb* pRB); 5984 MA_API void ma_rb_reset(ma_rb* pRB); 5985 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); 5986 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); 5987 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); 5988 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); 5989 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); 5990 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); 5991 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ 5992 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); 5993 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); 5994 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); 5995 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); 5996 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); 5997 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); 5998 5999 6000 typedef struct 6001 { 6002 ma_data_source_base ds; 6003 ma_rb rb; 6004 ma_format format; 6005 ma_uint32 channels; 6006 ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ 6007 } ma_pcm_rb; 6008 6009 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); 6010 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); 6011 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); 6012 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); 6013 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); 6014 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); 6015 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); 6016 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); 6017 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); 6018 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); 6019 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ 6020 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); 6021 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); 6022 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); 6023 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); 6024 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); 6025 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); 6026 MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); 6027 MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); 6028 MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); 6029 MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); 6030 6031 6032 /* 6033 The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The 6034 capture device writes to it, and then a playback device reads from it. 6035 6036 At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly 6037 handle desyncs. Note that the API is work in progress and may change at any time in any version. 6038 6039 The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size 6040 in frames. The internal sample rate of the capture device is also needed in order to calculate the size. 6041 */ 6042 typedef struct 6043 { 6044 ma_pcm_rb rb; 6045 } ma_duplex_rb; 6046 6047 MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); 6048 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); 6049 6050 6051 /************************************************************************************************************************************************************ 6052 6053 Miscellaneous Helpers 6054 6055 ************************************************************************************************************************************************************/ 6056 /* 6057 Retrieves a human readable description of the given result code. 6058 */ 6059 MA_API const char* ma_result_description(ma_result result); 6060 6061 /* 6062 malloc() 6063 */ 6064 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); 6065 6066 /* 6067 calloc() 6068 */ 6069 MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); 6070 6071 /* 6072 realloc() 6073 */ 6074 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); 6075 6076 /* 6077 free() 6078 */ 6079 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); 6080 6081 /* 6082 Performs an aligned malloc, with the assumption that the alignment is a power of 2. 6083 */ 6084 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); 6085 6086 /* 6087 Free's an aligned malloc'd buffer. 6088 */ 6089 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); 6090 6091 /* 6092 Retrieves a friendly name for a format. 6093 */ 6094 MA_API const char* ma_get_format_name(ma_format format); 6095 6096 /* 6097 Blends two frames in floating point format. 6098 */ 6099 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); 6100 6101 /* 6102 Retrieves the size of a sample in bytes for the given format. 6103 6104 This API is efficient and is implemented using a lookup table. 6105 6106 Thread Safety: SAFE 6107 This API is pure. 6108 */ 6109 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); 6110 static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } 6111 6112 /* 6113 Converts a log level to a string. 6114 */ 6115 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); 6116 6117 6118 6119 6120 /************************************************************************************************************************************************************ 6121 6122 Synchronization 6123 6124 ************************************************************************************************************************************************************/ 6125 /* 6126 Locks a spinlock. 6127 */ 6128 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); 6129 6130 /* 6131 Locks a spinlock, but does not yield() when looping. 6132 */ 6133 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); 6134 6135 /* 6136 Unlocks a spinlock. 6137 */ 6138 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); 6139 6140 6141 #ifndef MA_NO_THREADING 6142 6143 /* 6144 Creates a mutex. 6145 6146 A mutex must be created from a valid context. A mutex is initially unlocked. 6147 */ 6148 MA_API ma_result ma_mutex_init(ma_mutex* pMutex); 6149 6150 /* 6151 Deletes a mutex. 6152 */ 6153 MA_API void ma_mutex_uninit(ma_mutex* pMutex); 6154 6155 /* 6156 Locks a mutex with an infinite timeout. 6157 */ 6158 MA_API void ma_mutex_lock(ma_mutex* pMutex); 6159 6160 /* 6161 Unlocks a mutex. 6162 */ 6163 MA_API void ma_mutex_unlock(ma_mutex* pMutex); 6164 6165 6166 /* 6167 Initializes an auto-reset event. 6168 */ 6169 MA_API ma_result ma_event_init(ma_event* pEvent); 6170 6171 /* 6172 Uninitializes an auto-reset event. 6173 */ 6174 MA_API void ma_event_uninit(ma_event* pEvent); 6175 6176 /* 6177 Waits for the specified auto-reset event to become signalled. 6178 */ 6179 MA_API ma_result ma_event_wait(ma_event* pEvent); 6180 6181 /* 6182 Signals the specified auto-reset event. 6183 */ 6184 MA_API ma_result ma_event_signal(ma_event* pEvent); 6185 #endif /* MA_NO_THREADING */ 6186 6187 6188 /* 6189 Fence 6190 ===== 6191 This locks while the counter is larger than 0. Counter can be incremented and decremented by any 6192 thread, but care needs to be taken when waiting. It is possible for one thread to acquire the 6193 fence just as another thread returns from ma_fence_wait(). 6194 6195 The idea behind a fence is to allow you to wait for a group of operations to complete. When an 6196 operation starts, the counter is incremented which locks the fence. When the operation completes, 6197 the fence will be released which decrements the counter. ma_fence_wait() will block until the 6198 counter hits zero. 6199 6200 If threading is disabled, ma_fence_wait() will spin on the counter. 6201 */ 6202 typedef struct 6203 { 6204 #ifndef MA_NO_THREADING 6205 ma_event e; 6206 #endif 6207 ma_uint32 counter; 6208 } ma_fence; 6209 6210 MA_API ma_result ma_fence_init(ma_fence* pFence); 6211 MA_API void ma_fence_uninit(ma_fence* pFence); 6212 MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ 6213 MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ 6214 MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ 6215 6216 6217 6218 /* 6219 Notification callback for asynchronous operations. 6220 */ 6221 typedef void ma_async_notification; 6222 6223 typedef struct 6224 { 6225 void (* onSignal)(ma_async_notification* pNotification); 6226 } ma_async_notification_callbacks; 6227 6228 MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); 6229 6230 6231 /* 6232 Simple polling notification. 6233 6234 This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() 6235 */ 6236 typedef struct 6237 { 6238 ma_async_notification_callbacks cb; 6239 ma_bool32 signalled; 6240 } ma_async_notification_poll; 6241 6242 MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); 6243 MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); 6244 6245 6246 /* 6247 Event Notification 6248 6249 This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. 6250 */ 6251 typedef struct 6252 { 6253 ma_async_notification_callbacks cb; 6254 #ifndef MA_NO_THREADING 6255 ma_event e; 6256 #endif 6257 } ma_async_notification_event; 6258 6259 MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); 6260 MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); 6261 MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); 6262 MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); 6263 6264 6265 6266 6267 /************************************************************************************************************************************************************ 6268 6269 Job Queue 6270 6271 ************************************************************************************************************************************************************/ 6272 6273 /* 6274 Slot Allocator 6275 -------------- 6276 The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used 6277 as the insertion point for an object. 6278 6279 Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. 6280 6281 The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: 6282 6283 +-----------------+-----------------+ 6284 | 32 Bits | 32 Bits | 6285 +-----------------+-----------------+ 6286 | Reference Count | Slot Index | 6287 +-----------------+-----------------+ 6288 */ 6289 typedef struct 6290 { 6291 ma_uint32 capacity; /* The number of slots to make available. */ 6292 } ma_slot_allocator_config; 6293 6294 MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); 6295 6296 6297 typedef struct 6298 { 6299 MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ 6300 } ma_slot_allocator_group; 6301 6302 typedef struct 6303 { 6304 ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ 6305 ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ 6306 ma_uint32 count; /* Allocation count. */ 6307 ma_uint32 capacity; 6308 6309 /* Memory management. */ 6310 ma_bool32 _ownsHeap; 6311 void* _pHeap; 6312 } ma_slot_allocator; 6313 6314 MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); 6315 MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); 6316 MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); 6317 MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); 6318 MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); 6319 MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); 6320 6321 6322 typedef struct ma_job ma_job; 6323 6324 /* 6325 Callback for processing a job. Each job type will have their own processing callback which will be 6326 called by ma_job_process(). 6327 */ 6328 typedef ma_result (* ma_job_proc)(ma_job* pJob); 6329 6330 /* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ 6331 typedef enum 6332 { 6333 /* Miscellaneous. */ 6334 MA_JOB_TYPE_QUIT = 0, 6335 MA_JOB_TYPE_CUSTOM, 6336 6337 /* Resource Manager. */ 6338 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, 6339 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, 6340 MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, 6341 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, 6342 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, 6343 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, 6344 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, 6345 MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, 6346 MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, 6347 6348 /* Device. */ 6349 MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, 6350 6351 /* Count. Must always be last. */ 6352 MA_JOB_TYPE_COUNT 6353 } ma_job_type; 6354 6355 struct ma_job 6356 { 6357 union 6358 { 6359 struct 6360 { 6361 ma_uint16 code; /* Job type. */ 6362 ma_uint16 slot; /* Index into a ma_slot_allocator. */ 6363 ma_uint32 refcount; 6364 } breakup; 6365 ma_uint64 allocation; 6366 } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ 6367 MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ 6368 ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ 6369 6370 union 6371 { 6372 /* Miscellaneous. */ 6373 struct 6374 { 6375 ma_job_proc proc; 6376 ma_uintptr data0; 6377 ma_uintptr data1; 6378 } custom; 6379 6380 /* Resource Manager */ 6381 union 6382 { 6383 struct 6384 { 6385 /*ma_resource_manager**/ void* pResourceManager; 6386 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; 6387 char* pFilePath; 6388 wchar_t* pFilePathW; 6389 ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ 6390 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ 6391 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ 6392 ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ 6393 ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ 6394 } loadDataBufferNode; 6395 struct 6396 { 6397 /*ma_resource_manager**/ void* pResourceManager; 6398 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; 6399 ma_async_notification* pDoneNotification; 6400 ma_fence* pDoneFence; 6401 } freeDataBufferNode; 6402 struct 6403 { 6404 /*ma_resource_manager**/ void* pResourceManager; 6405 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; 6406 /*ma_decoder**/ void* pDecoder; 6407 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ 6408 ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ 6409 } pageDataBufferNode; 6410 6411 struct 6412 { 6413 /*ma_resource_manager_data_buffer**/ void* pDataBuffer; 6414 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ 6415 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ 6416 ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ 6417 ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ 6418 ma_uint64 rangeBegInPCMFrames; 6419 ma_uint64 rangeEndInPCMFrames; 6420 ma_uint64 loopPointBegInPCMFrames; 6421 ma_uint64 loopPointEndInPCMFrames; 6422 ma_uint32 isLooping; 6423 } loadDataBuffer; 6424 struct 6425 { 6426 /*ma_resource_manager_data_buffer**/ void* pDataBuffer; 6427 ma_async_notification* pDoneNotification; 6428 ma_fence* pDoneFence; 6429 } freeDataBuffer; 6430 6431 struct 6432 { 6433 /*ma_resource_manager_data_stream**/ void* pDataStream; 6434 char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ 6435 wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ 6436 ma_uint64 initialSeekPoint; 6437 ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ 6438 ma_fence* pInitFence; 6439 } loadDataStream; 6440 struct 6441 { 6442 /*ma_resource_manager_data_stream**/ void* pDataStream; 6443 ma_async_notification* pDoneNotification; 6444 ma_fence* pDoneFence; 6445 } freeDataStream; 6446 struct 6447 { 6448 /*ma_resource_manager_data_stream**/ void* pDataStream; 6449 ma_uint32 pageIndex; /* The index of the page to decode into. */ 6450 } pageDataStream; 6451 struct 6452 { 6453 /*ma_resource_manager_data_stream**/ void* pDataStream; 6454 ma_uint64 frameIndex; 6455 } seekDataStream; 6456 } resourceManager; 6457 6458 /* Device. */ 6459 union 6460 { 6461 union 6462 { 6463 struct 6464 { 6465 /*ma_device**/ void* pDevice; 6466 /*ma_device_type*/ ma_uint32 deviceType; 6467 } reroute; 6468 } aaudio; 6469 } device; 6470 } data; 6471 }; 6472 6473 MA_API ma_job ma_job_init(ma_uint16 code); 6474 MA_API ma_result ma_job_process(ma_job* pJob); 6475 6476 6477 /* 6478 When set, ma_job_queue_next() will not wait and no semaphore will be signaled in 6479 ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. 6480 6481 This flag should always be used for platforms that do not support multithreading. 6482 */ 6483 typedef enum 6484 { 6485 MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 6486 } ma_job_queue_flags; 6487 6488 typedef struct 6489 { 6490 ma_uint32 flags; 6491 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ 6492 } ma_job_queue_config; 6493 6494 MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); 6495 6496 6497 typedef struct 6498 { 6499 ma_uint32 flags; /* Flags passed in at initialization time. */ 6500 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ 6501 MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ 6502 MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ 6503 #ifndef MA_NO_THREADING 6504 ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ 6505 #endif 6506 ma_slot_allocator allocator; 6507 ma_job* pJobs; 6508 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 6509 ma_spinlock lock; 6510 #endif 6511 6512 /* Memory management. */ 6513 void* _pHeap; 6514 ma_bool32 _ownsHeap; 6515 } ma_job_queue; 6516 6517 MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); 6518 MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); 6519 MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); 6520 MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); 6521 MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); 6522 MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ 6523 6524 6525 6526 /************************************************************************************************************************************************************ 6527 ************************************************************************************************************************************************************* 6528 6529 DEVICE I/O 6530 ========== 6531 6532 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. 6533 6534 ************************************************************************************************************************************************************* 6535 ************************************************************************************************************************************************************/ 6536 #ifndef MA_NO_DEVICE_IO 6537 /* Some backends are only supported on certain platforms. */ 6538 #if defined(MA_WIN32) 6539 #define MA_SUPPORT_WASAPI 6540 6541 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ 6542 #define MA_SUPPORT_DSOUND 6543 #define MA_SUPPORT_WINMM 6544 6545 /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ 6546 #if !defined(__COSMOPOLITAN__) 6547 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ 6548 #endif 6549 #endif 6550 #endif 6551 #if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) 6552 #if defined(MA_LINUX) 6553 #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ 6554 #define MA_SUPPORT_ALSA 6555 #endif 6556 #endif 6557 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) 6558 #define MA_SUPPORT_PULSEAUDIO 6559 #define MA_SUPPORT_JACK 6560 #endif 6561 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ 6562 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ 6563 #endif 6564 #if defined(__NetBSD__) || defined(__OpenBSD__) 6565 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ 6566 #endif 6567 #if defined(__FreeBSD__) || defined(__DragonFly__) 6568 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ 6569 #endif 6570 #endif 6571 #if defined(MA_ANDROID) 6572 #define MA_SUPPORT_AAUDIO 6573 #define MA_SUPPORT_OPENSL 6574 #endif 6575 #if defined(MA_APPLE) 6576 #define MA_SUPPORT_COREAUDIO 6577 #endif 6578 #if defined(MA_EMSCRIPTEN) 6579 #define MA_SUPPORT_WEBAUDIO 6580 #endif 6581 6582 /* All platforms should support custom backends. */ 6583 #define MA_SUPPORT_CUSTOM 6584 6585 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ 6586 #if !defined(MA_EMSCRIPTEN) 6587 #define MA_SUPPORT_NULL 6588 #endif 6589 6590 6591 #if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) 6592 #define MA_HAS_WASAPI 6593 #endif 6594 #if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) 6595 #define MA_HAS_DSOUND 6596 #endif 6597 #if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) 6598 #define MA_HAS_WINMM 6599 #endif 6600 #if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) 6601 #define MA_HAS_ALSA 6602 #endif 6603 #if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) 6604 #define MA_HAS_PULSEAUDIO 6605 #endif 6606 #if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) 6607 #define MA_HAS_JACK 6608 #endif 6609 #if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) 6610 #define MA_HAS_COREAUDIO 6611 #endif 6612 #if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) 6613 #define MA_HAS_SNDIO 6614 #endif 6615 #if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) 6616 #define MA_HAS_AUDIO4 6617 #endif 6618 #if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) 6619 #define MA_HAS_OSS 6620 #endif 6621 #if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) 6622 #define MA_HAS_AAUDIO 6623 #endif 6624 #if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) 6625 #define MA_HAS_OPENSL 6626 #endif 6627 #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) 6628 #define MA_HAS_WEBAUDIO 6629 #endif 6630 #if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) 6631 #define MA_HAS_CUSTOM 6632 #endif 6633 #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) 6634 #define MA_HAS_NULL 6635 #endif 6636 6637 typedef enum 6638 { 6639 ma_device_state_uninitialized = 0, 6640 ma_device_state_stopped = 1, /* The device's default state after initialization. */ 6641 ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ 6642 ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ 6643 ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ 6644 } ma_device_state; 6645 6646 MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) 6647 6648 6649 #ifdef MA_SUPPORT_WASAPI 6650 /* We need a IMMNotificationClient object for WASAPI. */ 6651 typedef struct 6652 { 6653 void* lpVtbl; 6654 ma_uint32 counter; 6655 ma_device* pDevice; 6656 } ma_IMMNotificationClient; 6657 #endif 6658 6659 /* Backend enums must be in priority order. */ 6660 typedef enum 6661 { 6662 ma_backend_wasapi, 6663 ma_backend_dsound, 6664 ma_backend_winmm, 6665 ma_backend_coreaudio, 6666 ma_backend_sndio, 6667 ma_backend_audio4, 6668 ma_backend_oss, 6669 ma_backend_pulseaudio, 6670 ma_backend_alsa, 6671 ma_backend_jack, 6672 ma_backend_aaudio, 6673 ma_backend_opensl, 6674 ma_backend_webaudio, 6675 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ 6676 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ 6677 } ma_backend; 6678 6679 #define MA_BACKEND_COUNT (ma_backend_null+1) 6680 6681 6682 /* 6683 Device job thread. This is used by backends that require asynchronous processing of certain 6684 operations. It is not used by all backends. 6685 6686 The device job thread is made up of a thread and a job queue. You can post a job to the thread with 6687 ma_device_job_thread_post(). The thread will do the processing of the job. 6688 */ 6689 typedef struct 6690 { 6691 ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ 6692 ma_uint32 jobQueueCapacity; 6693 ma_uint32 jobQueueFlags; 6694 } ma_device_job_thread_config; 6695 6696 MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); 6697 6698 typedef struct 6699 { 6700 ma_thread thread; 6701 ma_job_queue jobQueue; 6702 ma_bool32 _hasThread; 6703 } ma_device_job_thread; 6704 6705 MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); 6706 MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); 6707 MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); 6708 MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); 6709 6710 6711 6712 /* Device notification types. */ 6713 typedef enum 6714 { 6715 ma_device_notification_type_started, 6716 ma_device_notification_type_stopped, 6717 ma_device_notification_type_rerouted, 6718 ma_device_notification_type_interruption_began, 6719 ma_device_notification_type_interruption_ended, 6720 ma_device_notification_type_unlocked 6721 } ma_device_notification_type; 6722 6723 typedef struct 6724 { 6725 ma_device* pDevice; 6726 ma_device_notification_type type; 6727 union 6728 { 6729 struct 6730 { 6731 int _unused; 6732 } started; 6733 struct 6734 { 6735 int _unused; 6736 } stopped; 6737 struct 6738 { 6739 int _unused; 6740 } rerouted; 6741 struct 6742 { 6743 int _unused; 6744 } interruption; 6745 } data; 6746 } ma_device_notification; 6747 6748 /* 6749 The notification callback for when the application should be notified of a change to the device. 6750 6751 This callback is used for notifying the application of changes such as when the device has started, 6752 stopped, rerouted or an interruption has occurred. Note that not all backends will post all 6753 notification types. For example, some backends will perform automatic stream routing without any 6754 kind of notification to the host program which means miniaudio will never know about it and will 6755 never be able to fire the rerouted notification. You should keep this in mind when designing your 6756 program. 6757 6758 The stopped notification will *not* get fired when a device is rerouted. 6759 6760 6761 Parameters 6762 ---------- 6763 pNotification (in) 6764 A pointer to a structure containing information about the event. Use the `pDevice` member of 6765 this object to retrieve the relevant device. The `type` member can be used to discriminate 6766 against each of the notification types. 6767 6768 6769 Remarks 6770 ------- 6771 Do not restart or uninitialize the device from the callback. 6772 6773 Not all notifications will be triggered by all backends, however the started and stopped events 6774 should be reliable for all backends. Some backends do not have a good way to detect device 6775 stoppages due to unplugging the device which may result in the stopped callback not getting 6776 fired. This has been observed with at least one BSD variant. 6777 6778 The rerouted notification is fired *after* the reroute has occurred. The stopped notification will 6779 *not* get fired when a device is rerouted. The following backends are known to do automatic stream 6780 rerouting, but do not have a way to be notified of the change: 6781 6782 * DirectSound 6783 6784 The interruption notifications are used on mobile platforms for detecting when audio is interrupted 6785 due to things like an incoming phone call. Currently this is only implemented on iOS. None of the 6786 Android backends will report this notification. 6787 */ 6788 typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); 6789 6790 6791 /* 6792 The callback for processing audio data from the device. 6793 6794 The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data 6795 available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the 6796 callback will be fired with a consistent frame count. 6797 6798 6799 Parameters 6800 ---------- 6801 pDevice (in) 6802 A pointer to the relevant device. 6803 6804 pOutput (out) 6805 A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or 6806 full-duplex device and null for a capture and loopback device. 6807 6808 pInput (in) 6809 A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a 6810 playback device. 6811 6812 frameCount (in) 6813 The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The 6814 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must 6815 not assume this will always be the same value each time the callback is fired. 6816 6817 6818 Remarks 6819 ------- 6820 You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the 6821 callback. The following APIs cannot be called from inside the callback: 6822 6823 ma_device_init() 6824 ma_device_init_ex() 6825 ma_device_uninit() 6826 ma_device_start() 6827 ma_device_stop() 6828 6829 The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. 6830 */ 6831 typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); 6832 6833 6834 6835 6836 /* 6837 DEPRECATED. Use ma_device_notification_proc instead. 6838 6839 The callback for when the device has been stopped. 6840 6841 This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces 6842 such as being unplugged or an internal error occurring. 6843 6844 6845 Parameters 6846 ---------- 6847 pDevice (in) 6848 A pointer to the device that has just stopped. 6849 6850 6851 Remarks 6852 ------- 6853 Do not restart or uninitialize the device from the callback. 6854 */ 6855 typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ 6856 6857 typedef enum 6858 { 6859 ma_device_type_playback = 1, 6860 ma_device_type_capture = 2, 6861 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ 6862 ma_device_type_loopback = 4 6863 } ma_device_type; 6864 6865 typedef enum 6866 { 6867 ma_share_mode_shared = 0, 6868 ma_share_mode_exclusive 6869 } ma_share_mode; 6870 6871 /* iOS/tvOS/watchOS session categories. */ 6872 typedef enum 6873 { 6874 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ 6875 ma_ios_session_category_none, /* Leave the session category unchanged. */ 6876 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ 6877 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ 6878 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ 6879 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ 6880 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ 6881 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ 6882 } ma_ios_session_category; 6883 6884 /* iOS/tvOS/watchOS session category options */ 6885 typedef enum 6886 { 6887 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ 6888 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ 6889 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ 6890 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ 6891 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ 6892 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ 6893 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ 6894 } ma_ios_session_category_option; 6895 6896 /* OpenSL stream types. */ 6897 typedef enum 6898 { 6899 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ 6900 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ 6901 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ 6902 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ 6903 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ 6904 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ 6905 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ 6906 } ma_opensl_stream_type; 6907 6908 /* OpenSL recording presets. */ 6909 typedef enum 6910 { 6911 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ 6912 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ 6913 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ 6914 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ 6915 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ 6916 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ 6917 } ma_opensl_recording_preset; 6918 6919 /* WASAPI audio thread priority characteristics. */ 6920 typedef enum 6921 { 6922 ma_wasapi_usage_default = 0, 6923 ma_wasapi_usage_games, 6924 ma_wasapi_usage_pro_audio, 6925 } ma_wasapi_usage; 6926 6927 /* AAudio usage types. */ 6928 typedef enum 6929 { 6930 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ 6931 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ 6932 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ 6933 ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ 6934 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ 6935 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ 6936 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ 6937 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ 6938 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ 6939 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ 6940 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ 6941 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ 6942 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ 6943 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ 6944 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ 6945 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ 6946 ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ 6947 } ma_aaudio_usage; 6948 6949 /* AAudio content types. */ 6950 typedef enum 6951 { 6952 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ 6953 ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ 6954 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ 6955 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ 6956 ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ 6957 } ma_aaudio_content_type; 6958 6959 /* AAudio input presets. */ 6960 typedef enum 6961 { 6962 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ 6963 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ 6964 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ 6965 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ 6966 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ 6967 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ 6968 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ 6969 } ma_aaudio_input_preset; 6970 6971 typedef enum 6972 { 6973 ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ 6974 ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ 6975 ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ 6976 ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ 6977 } ma_aaudio_allowed_capture_policy; 6978 6979 typedef union 6980 { 6981 ma_int64 counter; 6982 double counterD; 6983 } ma_timer; 6984 6985 typedef union 6986 { 6987 ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ 6988 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ 6989 /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ 6990 char alsa[256]; /* ALSA uses a name string for identification. */ 6991 char pulse[256]; /* PulseAudio uses a name string for identification. */ 6992 int jack; /* JACK always uses default devices. */ 6993 char coreaudio[256]; /* Core Audio uses a string for identification. */ 6994 char sndio[256]; /* "snd/0", etc. */ 6995 char audio4[256]; /* "/dev/audio", etc. */ 6996 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ 6997 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ 6998 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ 6999 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ 7000 union 7001 { 7002 int i; 7003 char s[256]; 7004 void* p; 7005 } custom; /* The custom backend could be anything. Give them a few options. */ 7006 int nullbackend; /* The null backend uses an integer for device IDs. */ 7007 } ma_device_id; 7008 7009 7010 typedef struct ma_context_config ma_context_config; 7011 typedef struct ma_device_config ma_device_config; 7012 typedef struct ma_backend_callbacks ma_backend_callbacks; 7013 7014 #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ 7015 7016 #ifndef MA_MAX_DEVICE_NAME_LENGTH 7017 #define MA_MAX_DEVICE_NAME_LENGTH 255 7018 #endif 7019 7020 typedef struct 7021 { 7022 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ 7023 ma_device_id id; 7024 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ 7025 ma_bool32 isDefault; 7026 7027 ma_uint32 nativeDataFormatCount; 7028 struct 7029 { 7030 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ 7031 ma_uint32 channels; /* If set to 0, all channels are supported. */ 7032 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ 7033 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ 7034 } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ 7035 } ma_device_info; 7036 7037 struct ma_device_config 7038 { 7039 ma_device_type deviceType; 7040 ma_uint32 sampleRate; 7041 ma_uint32 periodSizeInFrames; 7042 ma_uint32 periodSizeInMilliseconds; 7043 ma_uint32 periods; 7044 ma_performance_profile performanceProfile; 7045 ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ 7046 ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ 7047 ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ 7048 ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ 7049 ma_device_data_proc dataCallback; 7050 ma_device_notification_proc notificationCallback; 7051 ma_stop_proc stopCallback; 7052 void* pUserData; 7053 ma_resampler_config resampling; 7054 struct 7055 { 7056 const ma_device_id* pDeviceID; 7057 ma_format format; 7058 ma_uint32 channels; 7059 ma_channel* pChannelMap; 7060 ma_channel_mix_mode channelMixMode; 7061 ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ 7062 ma_share_mode shareMode; 7063 } playback; 7064 struct 7065 { 7066 const ma_device_id* pDeviceID; 7067 ma_format format; 7068 ma_uint32 channels; 7069 ma_channel* pChannelMap; 7070 ma_channel_mix_mode channelMixMode; 7071 ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ 7072 ma_share_mode shareMode; 7073 } capture; 7074 7075 struct 7076 { 7077 ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ 7078 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ 7079 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ 7080 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ 7081 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ 7082 ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ 7083 ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ 7084 } wasapi; 7085 struct 7086 { 7087 ma_bool32 noMMap; /* Disables MMap mode. */ 7088 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ 7089 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ 7090 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ 7091 } alsa; 7092 struct 7093 { 7094 const char* pStreamNamePlayback; 7095 const char* pStreamNameCapture; 7096 } pulse; 7097 struct 7098 { 7099 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ 7100 } coreaudio; 7101 struct 7102 { 7103 ma_opensl_stream_type streamType; 7104 ma_opensl_recording_preset recordingPreset; 7105 ma_bool32 enableCompatibilityWorkarounds; 7106 } opensl; 7107 struct 7108 { 7109 ma_aaudio_usage usage; 7110 ma_aaudio_content_type contentType; 7111 ma_aaudio_input_preset inputPreset; 7112 ma_aaudio_allowed_capture_policy allowedCapturePolicy; 7113 ma_bool32 noAutoStartAfterReroute; 7114 ma_bool32 enableCompatibilityWorkarounds; 7115 } aaudio; 7116 }; 7117 7118 7119 /* 7120 The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. 7121 7122 7123 Parameters 7124 ---------- 7125 pContext (in) 7126 A pointer to the context performing the enumeration. 7127 7128 deviceType (in) 7129 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. 7130 7131 pInfo (in) 7132 A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, 7133 only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which 7134 is too inefficient. 7135 7136 pUserData (in) 7137 The user data pointer passed into `ma_context_enumerate_devices()`. 7138 */ 7139 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); 7140 7141 7142 /* 7143 Describes some basic details about a playback or capture device. 7144 */ 7145 typedef struct 7146 { 7147 const ma_device_id* pDeviceID; 7148 ma_share_mode shareMode; 7149 ma_format format; 7150 ma_uint32 channels; 7151 ma_uint32 sampleRate; 7152 ma_channel channelMap[MA_MAX_CHANNELS]; 7153 ma_uint32 periodSizeInFrames; 7154 ma_uint32 periodSizeInMilliseconds; 7155 ma_uint32 periodCount; 7156 } ma_device_descriptor; 7157 7158 /* 7159 These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context 7160 to many devices. A device is created from a context. 7161 7162 The general flow goes like this: 7163 7164 1) A context is created with `onContextInit()` 7165 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. 7166 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. 7167 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was 7168 selected from device enumeration via `onContextEnumerateDevices()`. 7169 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` 7170 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call 7171 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by 7172 miniaudio internally. 7173 7174 Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the 7175 callbacks defined in this structure. 7176 7177 Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which 7178 physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the 7179 given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration 7180 needs to stop and the `onContextEnumerateDevices()` function returns with a success code. 7181 7182 Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, 7183 and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the 7184 case when the device ID is NULL, in which case information about the default device needs to be retrieved. 7185 7186 Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. 7187 This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a 7188 device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, 7189 the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to 7190 the requested format. The conversion between the format requested by the application and the device's native format will be handled 7191 internally by miniaudio. 7192 7193 On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's 7194 supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for 7195 sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to 7196 `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should 7197 inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period 7198 size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the 7199 sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` 7200 object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). 7201 7202 Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses 7203 asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. 7204 7205 The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit 7206 easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and 7207 `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the 7208 backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. 7209 This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. 7210 7211 If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback 7212 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. 7213 7214 The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been 7215 encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. 7216 7217 The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this 7218 callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated 7219 which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, 7220 look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to 7221 wake up the audio thread. 7222 7223 If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the 7224 `onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. 7225 */ 7226 struct ma_backend_callbacks 7227 { 7228 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); 7229 ma_result (* onContextUninit)(ma_context* pContext); 7230 ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); 7231 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); 7232 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); 7233 ma_result (* onDeviceUninit)(ma_device* pDevice); 7234 ma_result (* onDeviceStart)(ma_device* pDevice); 7235 ma_result (* onDeviceStop)(ma_device* pDevice); 7236 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); 7237 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); 7238 ma_result (* onDeviceDataLoop)(ma_device* pDevice); 7239 ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); 7240 ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); 7241 }; 7242 7243 struct ma_context_config 7244 { 7245 ma_log* pLog; 7246 ma_thread_priority threadPriority; 7247 size_t threadStackSize; 7248 void* pUserData; 7249 ma_allocation_callbacks allocationCallbacks; 7250 struct 7251 { 7252 ma_bool32 useVerboseDeviceEnumeration; 7253 } alsa; 7254 struct 7255 { 7256 const char* pApplicationName; 7257 const char* pServerName; 7258 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ 7259 } pulse; 7260 struct 7261 { 7262 ma_ios_session_category sessionCategory; 7263 ma_uint32 sessionCategoryOptions; 7264 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ 7265 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ 7266 } coreaudio; 7267 struct 7268 { 7269 const char* pClientName; 7270 ma_bool32 tryStartServer; 7271 } jack; 7272 ma_backend_callbacks custom; 7273 }; 7274 7275 /* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ 7276 typedef struct 7277 { 7278 int code; 7279 ma_event* pEvent; /* This will be signalled when the event is complete. */ 7280 union 7281 { 7282 struct 7283 { 7284 int _unused; 7285 } quit; 7286 struct 7287 { 7288 ma_device_type deviceType; 7289 void* pAudioClient; 7290 void** ppAudioClientService; 7291 ma_result* pResult; /* The result from creating the audio client service. */ 7292 } createAudioClient; 7293 struct 7294 { 7295 ma_device* pDevice; 7296 ma_device_type deviceType; 7297 } releaseAudioClient; 7298 } data; 7299 } ma_context_command__wasapi; 7300 7301 struct ma_context 7302 { 7303 ma_backend_callbacks callbacks; 7304 ma_backend backend; /* DirectSound, ALSA, etc. */ 7305 ma_log* pLog; 7306 ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ 7307 ma_thread_priority threadPriority; 7308 size_t threadStackSize; 7309 void* pUserData; 7310 ma_allocation_callbacks allocationCallbacks; 7311 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ 7312 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ 7313 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ 7314 ma_uint32 playbackDeviceInfoCount; 7315 ma_uint32 captureDeviceInfoCount; 7316 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ 7317 7318 union 7319 { 7320 #ifdef MA_SUPPORT_WASAPI 7321 struct 7322 { 7323 ma_thread commandThread; 7324 ma_mutex commandLock; 7325 ma_semaphore commandSem; 7326 ma_uint32 commandIndex; 7327 ma_uint32 commandCount; 7328 ma_context_command__wasapi commands[4]; 7329 ma_handle hAvrt; 7330 ma_proc AvSetMmThreadCharacteristicsA; 7331 ma_proc AvRevertMmThreadcharacteristics; 7332 ma_handle hMMDevapi; 7333 ma_proc ActivateAudioInterfaceAsync; 7334 } wasapi; 7335 #endif 7336 #ifdef MA_SUPPORT_DSOUND 7337 struct 7338 { 7339 ma_handle hDSoundDLL; 7340 ma_proc DirectSoundCreate; 7341 ma_proc DirectSoundEnumerateA; 7342 ma_proc DirectSoundCaptureCreate; 7343 ma_proc DirectSoundCaptureEnumerateA; 7344 } dsound; 7345 #endif 7346 #ifdef MA_SUPPORT_WINMM 7347 struct 7348 { 7349 ma_handle hWinMM; 7350 ma_proc waveOutGetNumDevs; 7351 ma_proc waveOutGetDevCapsA; 7352 ma_proc waveOutOpen; 7353 ma_proc waveOutClose; 7354 ma_proc waveOutPrepareHeader; 7355 ma_proc waveOutUnprepareHeader; 7356 ma_proc waveOutWrite; 7357 ma_proc waveOutReset; 7358 ma_proc waveInGetNumDevs; 7359 ma_proc waveInGetDevCapsA; 7360 ma_proc waveInOpen; 7361 ma_proc waveInClose; 7362 ma_proc waveInPrepareHeader; 7363 ma_proc waveInUnprepareHeader; 7364 ma_proc waveInAddBuffer; 7365 ma_proc waveInStart; 7366 ma_proc waveInReset; 7367 } winmm; 7368 #endif 7369 #ifdef MA_SUPPORT_ALSA 7370 struct 7371 { 7372 ma_handle asoundSO; 7373 ma_proc snd_pcm_open; 7374 ma_proc snd_pcm_close; 7375 ma_proc snd_pcm_hw_params_sizeof; 7376 ma_proc snd_pcm_hw_params_any; 7377 ma_proc snd_pcm_hw_params_set_format; 7378 ma_proc snd_pcm_hw_params_set_format_first; 7379 ma_proc snd_pcm_hw_params_get_format_mask; 7380 ma_proc snd_pcm_hw_params_set_channels; 7381 ma_proc snd_pcm_hw_params_set_channels_near; 7382 ma_proc snd_pcm_hw_params_set_channels_minmax; 7383 ma_proc snd_pcm_hw_params_set_rate_resample; 7384 ma_proc snd_pcm_hw_params_set_rate; 7385 ma_proc snd_pcm_hw_params_set_rate_near; 7386 ma_proc snd_pcm_hw_params_set_buffer_size_near; 7387 ma_proc snd_pcm_hw_params_set_periods_near; 7388 ma_proc snd_pcm_hw_params_set_access; 7389 ma_proc snd_pcm_hw_params_get_format; 7390 ma_proc snd_pcm_hw_params_get_channels; 7391 ma_proc snd_pcm_hw_params_get_channels_min; 7392 ma_proc snd_pcm_hw_params_get_channels_max; 7393 ma_proc snd_pcm_hw_params_get_rate; 7394 ma_proc snd_pcm_hw_params_get_rate_min; 7395 ma_proc snd_pcm_hw_params_get_rate_max; 7396 ma_proc snd_pcm_hw_params_get_buffer_size; 7397 ma_proc snd_pcm_hw_params_get_periods; 7398 ma_proc snd_pcm_hw_params_get_access; 7399 ma_proc snd_pcm_hw_params_test_format; 7400 ma_proc snd_pcm_hw_params_test_channels; 7401 ma_proc snd_pcm_hw_params_test_rate; 7402 ma_proc snd_pcm_hw_params; 7403 ma_proc snd_pcm_sw_params_sizeof; 7404 ma_proc snd_pcm_sw_params_current; 7405 ma_proc snd_pcm_sw_params_get_boundary; 7406 ma_proc snd_pcm_sw_params_set_avail_min; 7407 ma_proc snd_pcm_sw_params_set_start_threshold; 7408 ma_proc snd_pcm_sw_params_set_stop_threshold; 7409 ma_proc snd_pcm_sw_params; 7410 ma_proc snd_pcm_format_mask_sizeof; 7411 ma_proc snd_pcm_format_mask_test; 7412 ma_proc snd_pcm_get_chmap; 7413 ma_proc snd_pcm_state; 7414 ma_proc snd_pcm_prepare; 7415 ma_proc snd_pcm_start; 7416 ma_proc snd_pcm_drop; 7417 ma_proc snd_pcm_drain; 7418 ma_proc snd_pcm_reset; 7419 ma_proc snd_device_name_hint; 7420 ma_proc snd_device_name_get_hint; 7421 ma_proc snd_card_get_index; 7422 ma_proc snd_device_name_free_hint; 7423 ma_proc snd_pcm_mmap_begin; 7424 ma_proc snd_pcm_mmap_commit; 7425 ma_proc snd_pcm_recover; 7426 ma_proc snd_pcm_readi; 7427 ma_proc snd_pcm_writei; 7428 ma_proc snd_pcm_avail; 7429 ma_proc snd_pcm_avail_update; 7430 ma_proc snd_pcm_wait; 7431 ma_proc snd_pcm_nonblock; 7432 ma_proc snd_pcm_info; 7433 ma_proc snd_pcm_info_sizeof; 7434 ma_proc snd_pcm_info_get_name; 7435 ma_proc snd_pcm_poll_descriptors; 7436 ma_proc snd_pcm_poll_descriptors_count; 7437 ma_proc snd_pcm_poll_descriptors_revents; 7438 ma_proc snd_config_update_free_global; 7439 7440 ma_mutex internalDeviceEnumLock; 7441 ma_bool32 useVerboseDeviceEnumeration; 7442 } alsa; 7443 #endif 7444 #ifdef MA_SUPPORT_PULSEAUDIO 7445 struct 7446 { 7447 ma_handle pulseSO; 7448 ma_proc pa_mainloop_new; 7449 ma_proc pa_mainloop_free; 7450 ma_proc pa_mainloop_quit; 7451 ma_proc pa_mainloop_get_api; 7452 ma_proc pa_mainloop_iterate; 7453 ma_proc pa_mainloop_wakeup; 7454 ma_proc pa_threaded_mainloop_new; 7455 ma_proc pa_threaded_mainloop_free; 7456 ma_proc pa_threaded_mainloop_start; 7457 ma_proc pa_threaded_mainloop_stop; 7458 ma_proc pa_threaded_mainloop_lock; 7459 ma_proc pa_threaded_mainloop_unlock; 7460 ma_proc pa_threaded_mainloop_wait; 7461 ma_proc pa_threaded_mainloop_signal; 7462 ma_proc pa_threaded_mainloop_accept; 7463 ma_proc pa_threaded_mainloop_get_retval; 7464 ma_proc pa_threaded_mainloop_get_api; 7465 ma_proc pa_threaded_mainloop_in_thread; 7466 ma_proc pa_threaded_mainloop_set_name; 7467 ma_proc pa_context_new; 7468 ma_proc pa_context_unref; 7469 ma_proc pa_context_connect; 7470 ma_proc pa_context_disconnect; 7471 ma_proc pa_context_set_state_callback; 7472 ma_proc pa_context_get_state; 7473 ma_proc pa_context_get_sink_info_list; 7474 ma_proc pa_context_get_source_info_list; 7475 ma_proc pa_context_get_sink_info_by_name; 7476 ma_proc pa_context_get_source_info_by_name; 7477 ma_proc pa_operation_unref; 7478 ma_proc pa_operation_get_state; 7479 ma_proc pa_channel_map_init_extend; 7480 ma_proc pa_channel_map_valid; 7481 ma_proc pa_channel_map_compatible; 7482 ma_proc pa_stream_new; 7483 ma_proc pa_stream_unref; 7484 ma_proc pa_stream_connect_playback; 7485 ma_proc pa_stream_connect_record; 7486 ma_proc pa_stream_disconnect; 7487 ma_proc pa_stream_get_state; 7488 ma_proc pa_stream_get_sample_spec; 7489 ma_proc pa_stream_get_channel_map; 7490 ma_proc pa_stream_get_buffer_attr; 7491 ma_proc pa_stream_set_buffer_attr; 7492 ma_proc pa_stream_get_device_name; 7493 ma_proc pa_stream_set_write_callback; 7494 ma_proc pa_stream_set_read_callback; 7495 ma_proc pa_stream_set_suspended_callback; 7496 ma_proc pa_stream_set_moved_callback; 7497 ma_proc pa_stream_is_suspended; 7498 ma_proc pa_stream_flush; 7499 ma_proc pa_stream_drain; 7500 ma_proc pa_stream_is_corked; 7501 ma_proc pa_stream_cork; 7502 ma_proc pa_stream_trigger; 7503 ma_proc pa_stream_begin_write; 7504 ma_proc pa_stream_write; 7505 ma_proc pa_stream_peek; 7506 ma_proc pa_stream_drop; 7507 ma_proc pa_stream_writable_size; 7508 ma_proc pa_stream_readable_size; 7509 7510 /*pa_mainloop**/ ma_ptr pMainLoop; 7511 /*pa_context**/ ma_ptr pPulseContext; 7512 char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ 7513 char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ 7514 } pulse; 7515 #endif 7516 #ifdef MA_SUPPORT_JACK 7517 struct 7518 { 7519 ma_handle jackSO; 7520 ma_proc jack_client_open; 7521 ma_proc jack_client_close; 7522 ma_proc jack_client_name_size; 7523 ma_proc jack_set_process_callback; 7524 ma_proc jack_set_buffer_size_callback; 7525 ma_proc jack_on_shutdown; 7526 ma_proc jack_get_sample_rate; 7527 ma_proc jack_get_buffer_size; 7528 ma_proc jack_get_ports; 7529 ma_proc jack_activate; 7530 ma_proc jack_deactivate; 7531 ma_proc jack_connect; 7532 ma_proc jack_port_register; 7533 ma_proc jack_port_name; 7534 ma_proc jack_port_get_buffer; 7535 ma_proc jack_free; 7536 7537 char* pClientName; 7538 ma_bool32 tryStartServer; 7539 } jack; 7540 #endif 7541 #ifdef MA_SUPPORT_COREAUDIO 7542 struct 7543 { 7544 ma_handle hCoreFoundation; 7545 ma_proc CFStringGetCString; 7546 ma_proc CFRelease; 7547 7548 ma_handle hCoreAudio; 7549 ma_proc AudioObjectGetPropertyData; 7550 ma_proc AudioObjectGetPropertyDataSize; 7551 ma_proc AudioObjectSetPropertyData; 7552 ma_proc AudioObjectAddPropertyListener; 7553 ma_proc AudioObjectRemovePropertyListener; 7554 7555 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ 7556 ma_proc AudioComponentFindNext; 7557 ma_proc AudioComponentInstanceDispose; 7558 ma_proc AudioComponentInstanceNew; 7559 ma_proc AudioOutputUnitStart; 7560 ma_proc AudioOutputUnitStop; 7561 ma_proc AudioUnitAddPropertyListener; 7562 ma_proc AudioUnitGetPropertyInfo; 7563 ma_proc AudioUnitGetProperty; 7564 ma_proc AudioUnitSetProperty; 7565 ma_proc AudioUnitInitialize; 7566 ma_proc AudioUnitRender; 7567 7568 /*AudioComponent*/ ma_ptr component; 7569 ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ 7570 } coreaudio; 7571 #endif 7572 #ifdef MA_SUPPORT_SNDIO 7573 struct 7574 { 7575 ma_handle sndioSO; 7576 ma_proc sio_open; 7577 ma_proc sio_close; 7578 ma_proc sio_setpar; 7579 ma_proc sio_getpar; 7580 ma_proc sio_getcap; 7581 ma_proc sio_start; 7582 ma_proc sio_stop; 7583 ma_proc sio_read; 7584 ma_proc sio_write; 7585 ma_proc sio_onmove; 7586 ma_proc sio_nfds; 7587 ma_proc sio_pollfd; 7588 ma_proc sio_revents; 7589 ma_proc sio_eof; 7590 ma_proc sio_setvol; 7591 ma_proc sio_onvol; 7592 ma_proc sio_initpar; 7593 } sndio; 7594 #endif 7595 #ifdef MA_SUPPORT_AUDIO4 7596 struct 7597 { 7598 int _unused; 7599 } audio4; 7600 #endif 7601 #ifdef MA_SUPPORT_OSS 7602 struct 7603 { 7604 int versionMajor; 7605 int versionMinor; 7606 } oss; 7607 #endif 7608 #ifdef MA_SUPPORT_AAUDIO 7609 struct 7610 { 7611 ma_handle hAAudio; /* libaaudio.so */ 7612 ma_proc AAudio_createStreamBuilder; 7613 ma_proc AAudioStreamBuilder_delete; 7614 ma_proc AAudioStreamBuilder_setDeviceId; 7615 ma_proc AAudioStreamBuilder_setDirection; 7616 ma_proc AAudioStreamBuilder_setSharingMode; 7617 ma_proc AAudioStreamBuilder_setFormat; 7618 ma_proc AAudioStreamBuilder_setChannelCount; 7619 ma_proc AAudioStreamBuilder_setSampleRate; 7620 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; 7621 ma_proc AAudioStreamBuilder_setFramesPerDataCallback; 7622 ma_proc AAudioStreamBuilder_setDataCallback; 7623 ma_proc AAudioStreamBuilder_setErrorCallback; 7624 ma_proc AAudioStreamBuilder_setPerformanceMode; 7625 ma_proc AAudioStreamBuilder_setUsage; 7626 ma_proc AAudioStreamBuilder_setContentType; 7627 ma_proc AAudioStreamBuilder_setInputPreset; 7628 ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; 7629 ma_proc AAudioStreamBuilder_openStream; 7630 ma_proc AAudioStream_close; 7631 ma_proc AAudioStream_getState; 7632 ma_proc AAudioStream_waitForStateChange; 7633 ma_proc AAudioStream_getFormat; 7634 ma_proc AAudioStream_getChannelCount; 7635 ma_proc AAudioStream_getSampleRate; 7636 ma_proc AAudioStream_getBufferCapacityInFrames; 7637 ma_proc AAudioStream_getFramesPerDataCallback; 7638 ma_proc AAudioStream_getFramesPerBurst; 7639 ma_proc AAudioStream_requestStart; 7640 ma_proc AAudioStream_requestStop; 7641 ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ 7642 } aaudio; 7643 #endif 7644 #ifdef MA_SUPPORT_OPENSL 7645 struct 7646 { 7647 ma_handle libOpenSLES; 7648 ma_handle SL_IID_ENGINE; 7649 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; 7650 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; 7651 ma_handle SL_IID_RECORD; 7652 ma_handle SL_IID_PLAY; 7653 ma_handle SL_IID_OUTPUTMIX; 7654 ma_handle SL_IID_ANDROIDCONFIGURATION; 7655 ma_proc slCreateEngine; 7656 } opensl; 7657 #endif 7658 #ifdef MA_SUPPORT_WEBAUDIO 7659 struct 7660 { 7661 int _unused; 7662 } webaudio; 7663 #endif 7664 #ifdef MA_SUPPORT_NULL 7665 struct 7666 { 7667 int _unused; 7668 } null_backend; 7669 #endif 7670 }; 7671 7672 union 7673 { 7674 #if defined(MA_WIN32) 7675 struct 7676 { 7677 /*HMODULE*/ ma_handle hOle32DLL; 7678 ma_proc CoInitialize; 7679 ma_proc CoInitializeEx; 7680 ma_proc CoUninitialize; 7681 ma_proc CoCreateInstance; 7682 ma_proc CoTaskMemFree; 7683 ma_proc PropVariantClear; 7684 ma_proc StringFromGUID2; 7685 7686 /*HMODULE*/ ma_handle hUser32DLL; 7687 ma_proc GetForegroundWindow; 7688 ma_proc GetDesktopWindow; 7689 7690 /*HMODULE*/ ma_handle hAdvapi32DLL; 7691 ma_proc RegOpenKeyExA; 7692 ma_proc RegCloseKey; 7693 ma_proc RegQueryValueExA; 7694 7695 /*HRESULT*/ long CoInitializeResult; 7696 } win32; 7697 #endif 7698 #ifdef MA_POSIX 7699 struct 7700 { 7701 int _unused; 7702 } posix; 7703 #endif 7704 int _unused; 7705 }; 7706 }; 7707 7708 struct ma_device 7709 { 7710 ma_context* pContext; 7711 ma_device_type type; 7712 ma_uint32 sampleRate; 7713 ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ 7714 ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ 7715 ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ 7716 ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ 7717 void* pUserData; /* Application defined data. */ 7718 ma_mutex startStopLock; 7719 ma_event wakeupEvent; 7720 ma_event startEvent; 7721 ma_event stopEvent; 7722 ma_thread thread; 7723 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ 7724 ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ 7725 ma_bool8 noPreSilencedOutputBuffer; 7726 ma_bool8 noClip; 7727 ma_bool8 noDisableDenormals; 7728 ma_bool8 noFixedSizedCallback; 7729 ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ 7730 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ 7731 struct 7732 { 7733 ma_resample_algorithm algorithm; 7734 ma_resampling_backend_vtable* pBackendVTable; 7735 void* pBackendUserData; 7736 struct 7737 { 7738 ma_uint32 lpfOrder; 7739 } linear; 7740 } resampling; 7741 struct 7742 { 7743 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ 7744 ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ 7745 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ 7746 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ 7747 ma_format format; 7748 ma_uint32 channels; 7749 ma_channel channelMap[MA_MAX_CHANNELS]; 7750 ma_format internalFormat; 7751 ma_uint32 internalChannels; 7752 ma_uint32 internalSampleRate; 7753 ma_channel internalChannelMap[MA_MAX_CHANNELS]; 7754 ma_uint32 internalPeriodSizeInFrames; 7755 ma_uint32 internalPeriods; 7756 ma_channel_mix_mode channelMixMode; 7757 ma_bool32 calculateLFEFromSpatialChannels; 7758 ma_data_converter converter; 7759 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ 7760 ma_uint32 intermediaryBufferCap; 7761 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ 7762 void* pInputCache; /* In external format. Can be null. */ 7763 ma_uint64 inputCacheCap; 7764 ma_uint64 inputCacheConsumed; 7765 ma_uint64 inputCacheRemaining; 7766 } playback; 7767 struct 7768 { 7769 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ 7770 ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ 7771 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ 7772 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ 7773 ma_format format; 7774 ma_uint32 channels; 7775 ma_channel channelMap[MA_MAX_CHANNELS]; 7776 ma_format internalFormat; 7777 ma_uint32 internalChannels; 7778 ma_uint32 internalSampleRate; 7779 ma_channel internalChannelMap[MA_MAX_CHANNELS]; 7780 ma_uint32 internalPeriodSizeInFrames; 7781 ma_uint32 internalPeriods; 7782 ma_channel_mix_mode channelMixMode; 7783 ma_bool32 calculateLFEFromSpatialChannels; 7784 ma_data_converter converter; 7785 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ 7786 ma_uint32 intermediaryBufferCap; 7787 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ 7788 } capture; 7789 7790 union 7791 { 7792 #ifdef MA_SUPPORT_WASAPI 7793 struct 7794 { 7795 /*IAudioClient**/ ma_ptr pAudioClientPlayback; 7796 /*IAudioClient**/ ma_ptr pAudioClientCapture; 7797 /*IAudioRenderClient**/ ma_ptr pRenderClient; 7798 /*IAudioCaptureClient**/ ma_ptr pCaptureClient; 7799 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ 7800 ma_IMMNotificationClient notificationClient; 7801 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ 7802 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ 7803 ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ 7804 ma_uint32 actualBufferSizeInFramesCapture; 7805 ma_uint32 originalPeriodSizeInFrames; 7806 ma_uint32 originalPeriodSizeInMilliseconds; 7807 ma_uint32 originalPeriods; 7808 ma_performance_profile originalPerformanceProfile; 7809 ma_uint32 periodSizeInFramesPlayback; 7810 ma_uint32 periodSizeInFramesCapture; 7811 void* pMappedBufferCapture; 7812 ma_uint32 mappedBufferCaptureCap; 7813 ma_uint32 mappedBufferCaptureLen; 7814 void* pMappedBufferPlayback; 7815 ma_uint32 mappedBufferPlaybackCap; 7816 ma_uint32 mappedBufferPlaybackLen; 7817 ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ 7818 ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ 7819 ma_uint32 loopbackProcessID; 7820 ma_bool8 loopbackProcessExclude; 7821 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ 7822 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ 7823 ma_bool8 noHardwareOffloading; 7824 ma_bool8 allowCaptureAutoStreamRouting; 7825 ma_bool8 allowPlaybackAutoStreamRouting; 7826 ma_bool8 isDetachedPlayback; 7827 ma_bool8 isDetachedCapture; 7828 ma_wasapi_usage usage; 7829 void* hAvrtHandle; 7830 ma_mutex rerouteLock; 7831 } wasapi; 7832 #endif 7833 #ifdef MA_SUPPORT_DSOUND 7834 struct 7835 { 7836 /*LPDIRECTSOUND*/ ma_ptr pPlayback; 7837 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; 7838 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; 7839 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; 7840 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; 7841 } dsound; 7842 #endif 7843 #ifdef MA_SUPPORT_WINMM 7844 struct 7845 { 7846 /*HWAVEOUT*/ ma_handle hDevicePlayback; 7847 /*HWAVEIN*/ ma_handle hDeviceCapture; 7848 /*HANDLE*/ ma_handle hEventPlayback; 7849 /*HANDLE*/ ma_handle hEventCapture; 7850 ma_uint32 fragmentSizeInFrames; 7851 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ 7852 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ 7853 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ 7854 ma_uint32 headerFramesConsumedCapture; /* ^^^ */ 7855 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ 7856 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ 7857 ma_uint8* pIntermediaryBufferPlayback; 7858 ma_uint8* pIntermediaryBufferCapture; 7859 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ 7860 } winmm; 7861 #endif 7862 #ifdef MA_SUPPORT_ALSA 7863 struct 7864 { 7865 /*snd_pcm_t**/ ma_ptr pPCMPlayback; 7866 /*snd_pcm_t**/ ma_ptr pPCMCapture; 7867 /*struct pollfd**/ void* pPollDescriptorsPlayback; 7868 /*struct pollfd**/ void* pPollDescriptorsCapture; 7869 int pollDescriptorCountPlayback; 7870 int pollDescriptorCountCapture; 7871 int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ 7872 int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ 7873 ma_bool8 isUsingMMapPlayback; 7874 ma_bool8 isUsingMMapCapture; 7875 } alsa; 7876 #endif 7877 #ifdef MA_SUPPORT_PULSEAUDIO 7878 struct 7879 { 7880 /*pa_mainloop**/ ma_ptr pMainLoop; 7881 /*pa_context**/ ma_ptr pPulseContext; 7882 /*pa_stream**/ ma_ptr pStreamPlayback; 7883 /*pa_stream**/ ma_ptr pStreamCapture; 7884 } pulse; 7885 #endif 7886 #ifdef MA_SUPPORT_JACK 7887 struct 7888 { 7889 /*jack_client_t**/ ma_ptr pClient; 7890 /*jack_port_t**/ ma_ptr* ppPortsPlayback; 7891 /*jack_port_t**/ ma_ptr* ppPortsCapture; 7892 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ 7893 float* pIntermediaryBufferCapture; 7894 } jack; 7895 #endif 7896 #ifdef MA_SUPPORT_COREAUDIO 7897 struct 7898 { 7899 ma_uint32 deviceObjectIDPlayback; 7900 ma_uint32 deviceObjectIDCapture; 7901 /*AudioUnit*/ ma_ptr audioUnitPlayback; 7902 /*AudioUnit*/ ma_ptr audioUnitCapture; 7903 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ 7904 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ 7905 ma_event stopEvent; 7906 ma_uint32 originalPeriodSizeInFrames; 7907 ma_uint32 originalPeriodSizeInMilliseconds; 7908 ma_uint32 originalPeriods; 7909 ma_performance_profile originalPerformanceProfile; 7910 ma_bool32 isDefaultPlaybackDevice; 7911 ma_bool32 isDefaultCaptureDevice; 7912 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ 7913 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ 7914 void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ 7915 } coreaudio; 7916 #endif 7917 #ifdef MA_SUPPORT_SNDIO 7918 struct 7919 { 7920 ma_ptr handlePlayback; 7921 ma_ptr handleCapture; 7922 ma_bool32 isStartedPlayback; 7923 ma_bool32 isStartedCapture; 7924 } sndio; 7925 #endif 7926 #ifdef MA_SUPPORT_AUDIO4 7927 struct 7928 { 7929 int fdPlayback; 7930 int fdCapture; 7931 } audio4; 7932 #endif 7933 #ifdef MA_SUPPORT_OSS 7934 struct 7935 { 7936 int fdPlayback; 7937 int fdCapture; 7938 } oss; 7939 #endif 7940 #ifdef MA_SUPPORT_AAUDIO 7941 struct 7942 { 7943 /*AAudioStream**/ ma_ptr pStreamPlayback; 7944 /*AAudioStream**/ ma_ptr pStreamCapture; 7945 ma_aaudio_usage usage; 7946 ma_aaudio_content_type contentType; 7947 ma_aaudio_input_preset inputPreset; 7948 ma_aaudio_allowed_capture_policy allowedCapturePolicy; 7949 ma_bool32 noAutoStartAfterReroute; 7950 } aaudio; 7951 #endif 7952 #ifdef MA_SUPPORT_OPENSL 7953 struct 7954 { 7955 /*SLObjectItf*/ ma_ptr pOutputMixObj; 7956 /*SLOutputMixItf*/ ma_ptr pOutputMix; 7957 /*SLObjectItf*/ ma_ptr pAudioPlayerObj; 7958 /*SLPlayItf*/ ma_ptr pAudioPlayer; 7959 /*SLObjectItf*/ ma_ptr pAudioRecorderObj; 7960 /*SLRecordItf*/ ma_ptr pAudioRecorder; 7961 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; 7962 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; 7963 ma_bool32 isDrainingCapture; 7964 ma_bool32 isDrainingPlayback; 7965 ma_uint32 currentBufferIndexPlayback; 7966 ma_uint32 currentBufferIndexCapture; 7967 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ 7968 ma_uint8* pBufferCapture; 7969 } opensl; 7970 #endif 7971 #ifdef MA_SUPPORT_WEBAUDIO 7972 struct 7973 { 7974 /* AudioWorklets path. */ 7975 /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; 7976 /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; 7977 float* pIntermediaryBuffer; 7978 void* pStackBuffer; 7979 ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ 7980 int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ 7981 } webaudio; 7982 #endif 7983 #ifdef MA_SUPPORT_NULL 7984 struct 7985 { 7986 ma_thread deviceThread; 7987 ma_event operationEvent; 7988 ma_event operationCompletionEvent; 7989 ma_semaphore operationSemaphore; 7990 ma_uint32 operation; 7991 ma_result operationResult; 7992 ma_timer timer; 7993 double priorRunTime; 7994 ma_uint32 currentPeriodFramesRemainingPlayback; 7995 ma_uint32 currentPeriodFramesRemainingCapture; 7996 ma_uint64 lastProcessedFramePlayback; 7997 ma_uint64 lastProcessedFrameCapture; 7998 ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ 7999 } null_device; 8000 #endif 8001 }; 8002 }; 8003 #if defined(_MSC_VER) && !defined(__clang__) 8004 #pragma warning(pop) 8005 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 8006 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ 8007 #endif 8008 8009 /* 8010 Initializes a `ma_context_config` object. 8011 8012 8013 Return Value 8014 ------------ 8015 A `ma_context_config` initialized to defaults. 8016 8017 8018 Remarks 8019 ------- 8020 You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio 8021 is updated and new members are added to `ma_context_config`. It also sets logical defaults. 8022 8023 You can override members of the returned object by changing it's members directly. 8024 8025 8026 See Also 8027 -------- 8028 ma_context_init() 8029 */ 8030 MA_API ma_context_config ma_context_config_init(void); 8031 8032 /* 8033 Initializes a context. 8034 8035 The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual 8036 device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. 8037 8038 8039 Parameters 8040 ---------- 8041 backends (in, optional) 8042 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. 8043 8044 backendCount (in, optional) 8045 The number of items in `backend`. Ignored if `backend` is NULL. 8046 8047 pConfig (in, optional) 8048 The context configuration. 8049 8050 pContext (in) 8051 A pointer to the context object being initialized. 8052 8053 8054 Return Value 8055 ------------ 8056 MA_SUCCESS if successful; any other error code otherwise. 8057 8058 8059 Thread Safety 8060 ------------- 8061 Unsafe. Do not call this function across multiple threads as some backends read and write to global state. 8062 8063 8064 Remarks 8065 ------- 8066 When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: 8067 8068 |-------------|-----------------------|--------------------------------------------------------| 8069 | Name | Enum Name | Supported Operating Systems | 8070 |-------------|-----------------------|--------------------------------------------------------| 8071 | WASAPI | ma_backend_wasapi | Windows Vista+ | 8072 | DirectSound | ma_backend_dsound | Windows XP+ | 8073 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | 8074 | Core Audio | ma_backend_coreaudio | macOS, iOS | 8075 | ALSA | ma_backend_alsa | Linux | 8076 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | 8077 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | 8078 | sndio | ma_backend_sndio | OpenBSD | 8079 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | 8080 | OSS | ma_backend_oss | FreeBSD | 8081 | AAudio | ma_backend_aaudio | Android 8+ | 8082 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | 8083 | Web Audio | ma_backend_webaudio | Web (via Emscripten) | 8084 | Null | ma_backend_null | Cross Platform (not used on Web) | 8085 |-------------|-----------------------|--------------------------------------------------------| 8086 8087 The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings 8088 can then be set directly on the structure. Below are the members of the `ma_context_config` object. 8089 8090 pLog 8091 A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not 8092 require logging. See the `ma_log` API for details on how to use the logging system. 8093 8094 threadPriority 8095 The desired priority to use for the audio thread. Allowable values include the following: 8096 8097 |--------------------------------------| 8098 | Thread Priority | 8099 |--------------------------------------| 8100 | ma_thread_priority_idle | 8101 | ma_thread_priority_lowest | 8102 | ma_thread_priority_low | 8103 | ma_thread_priority_normal | 8104 | ma_thread_priority_high | 8105 | ma_thread_priority_highest (default) | 8106 | ma_thread_priority_realtime | 8107 | ma_thread_priority_default | 8108 |--------------------------------------| 8109 8110 threadStackSize 8111 The desired size of the stack for the audio thread. Defaults to the operating system's default. 8112 8113 pUserData 8114 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. 8115 8116 allocationCallbacks 8117 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation 8118 callbacks will be used for anything tied to the context, including devices. 8119 8120 alsa.useVerboseDeviceEnumeration 8121 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique 8122 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes 8123 it so the ALSA backend includes all devices. Defaults to false. 8124 8125 pulse.pApplicationName 8126 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. 8127 8128 pulse.pServerName 8129 PulseAudio only. The name of the server to connect to with `pa_context_connect()`. 8130 8131 pulse.tryAutoSpawn 8132 PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that 8133 miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be 8134 intrusive for the end user. 8135 8136 coreaudio.sessionCategory 8137 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. 8138 8139 |-----------------------------------------|-------------------------------------| 8140 | miniaudio Token | Core Audio Token | 8141 |-----------------------------------------|-------------------------------------| 8142 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | 8143 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | 8144 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | 8145 | ma_ios_session_category_record | AVAudioSessionCategoryRecord | 8146 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | 8147 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | 8148 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | 8149 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | 8150 |-----------------------------------------|-------------------------------------| 8151 8152 coreaudio.sessionCategoryOptions 8153 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. 8154 8155 |---------------------------------------------------------------------------|------------------------------------------------------------------| 8156 | miniaudio Token | Core Audio Token | 8157 |---------------------------------------------------------------------------|------------------------------------------------------------------| 8158 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | 8159 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | 8160 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | 8161 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | 8162 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | 8163 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | 8164 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | 8165 |---------------------------------------------------------------------------|------------------------------------------------------------------| 8166 8167 coreaudio.noAudioSessionActivate 8168 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. 8169 8170 coreaudio.noAudioSessionDeactivate 8171 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. 8172 8173 jack.pClientName 8174 The name of the client to pass to `jack_client_open()`. 8175 8176 jack.tryStartServer 8177 Whether or not to try auto-starting the JACK server. Defaults to false. 8178 8179 8180 It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the 8181 relevant backends every time it's initialized. 8182 8183 The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The 8184 reason for this is that a pointer to the context is stored in the `ma_device` structure. 8185 8186 8187 Example 1 - Default Initialization 8188 ---------------------------------- 8189 The example below shows how to initialize the context using the default configuration. 8190 8191 ```c 8192 ma_context context; 8193 ma_result result = ma_context_init(NULL, 0, NULL, &context); 8194 if (result != MA_SUCCESS) { 8195 // Error. 8196 } 8197 ``` 8198 8199 8200 Example 2 - Custom Configuration 8201 -------------------------------- 8202 The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program 8203 wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also 8204 want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. 8205 8206 For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. 8207 8208 ```c 8209 ma_backend backends[] = { 8210 ma_backend_alsa, 8211 ma_backend_pulseaudio, 8212 ma_backend_wasapi, 8213 ma_backend_dsound 8214 }; 8215 8216 ma_log log; 8217 ma_log_init(&log); 8218 ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); 8219 8220 ma_context_config config = ma_context_config_init(); 8221 config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. 8222 8223 ma_context context; 8224 ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); 8225 if (result != MA_SUCCESS) { 8226 // Error. 8227 if (result == MA_NO_BACKEND) { 8228 // Couldn't find an appropriate backend. 8229 } 8230 } 8231 8232 // You could also attach a log callback post-initialization: 8233 ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); 8234 ``` 8235 8236 8237 See Also 8238 -------- 8239 ma_context_config_init() 8240 ma_context_uninit() 8241 */ 8242 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); 8243 8244 /* 8245 Uninitializes a context. 8246 8247 8248 Return Value 8249 ------------ 8250 MA_SUCCESS if successful; any other error code otherwise. 8251 8252 8253 Thread Safety 8254 ------------- 8255 Unsafe. Do not call this function across multiple threads as some backends read and write to global state. 8256 8257 8258 Remarks 8259 ------- 8260 Results are undefined if you call this while any device created by this context is still active. 8261 8262 8263 See Also 8264 -------- 8265 ma_context_init() 8266 */ 8267 MA_API ma_result ma_context_uninit(ma_context* pContext); 8268 8269 /* 8270 Retrieves the size of the ma_context object. 8271 8272 This is mainly for the purpose of bindings to know how much memory to allocate. 8273 */ 8274 MA_API size_t ma_context_sizeof(void); 8275 8276 /* 8277 Retrieves a pointer to the log object associated with this context. 8278 8279 8280 Remarks 8281 ------- 8282 Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log 8283 message. 8284 8285 You can attach your own logging callback to the log with `ma_log_register_callback()` 8286 8287 8288 Return Value 8289 ------------ 8290 A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, 8291 NULL will be returned. 8292 */ 8293 MA_API ma_log* ma_context_get_log(ma_context* pContext); 8294 8295 /* 8296 Enumerates over every device (both playback and capture). 8297 8298 This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur 8299 an internal heap allocation, or it simply suits your code better. 8300 8301 Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require 8302 opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, 8303 but don't call it from within the enumeration callback. 8304 8305 Returning false from the callback will stop enumeration. Returning true will continue enumeration. 8306 8307 8308 Parameters 8309 ---------- 8310 pContext (in) 8311 A pointer to the context performing the enumeration. 8312 8313 callback (in) 8314 The callback to fire for each enumerated device. 8315 8316 pUserData (in) 8317 A pointer to application-defined data passed to the callback. 8318 8319 8320 Return Value 8321 ------------ 8322 MA_SUCCESS if successful; any other error code otherwise. 8323 8324 8325 Thread Safety 8326 ------------- 8327 Safe. This is guarded using a simple mutex lock. 8328 8329 8330 Remarks 8331 ------- 8332 Do _not_ assume the first enumerated device of a given type is the default device. 8333 8334 Some backends and platforms may only support default playback and capture devices. 8335 8336 In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, 8337 do not try to call `ma_context_get_device_info()` from within the callback. 8338 8339 Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. 8340 8341 8342 Example 1 - Simple Enumeration 8343 ------------------------------ 8344 ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) 8345 { 8346 printf("Device Name: %s\n", pInfo->name); 8347 return MA_TRUE; 8348 } 8349 8350 ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); 8351 if (result != MA_SUCCESS) { 8352 // Error. 8353 } 8354 8355 8356 See Also 8357 -------- 8358 ma_context_get_devices() 8359 */ 8360 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); 8361 8362 /* 8363 Retrieves basic information about every active playback and/or capture device. 8364 8365 This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` 8366 parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. 8367 8368 8369 Parameters 8370 ---------- 8371 pContext (in) 8372 A pointer to the context performing the enumeration. 8373 8374 ppPlaybackDeviceInfos (out) 8375 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. 8376 8377 pPlaybackDeviceCount (out) 8378 A pointer to an unsigned integer that will receive the number of playback devices. 8379 8380 ppCaptureDeviceInfos (out) 8381 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. 8382 8383 pCaptureDeviceCount (out) 8384 A pointer to an unsigned integer that will receive the number of capture devices. 8385 8386 8387 Return Value 8388 ------------ 8389 MA_SUCCESS if successful; any other error code otherwise. 8390 8391 8392 Thread Safety 8393 ------------- 8394 Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple 8395 threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. 8396 8397 8398 Remarks 8399 ------- 8400 It is _not_ safe to assume the first device in the list is the default device. 8401 8402 You can pass in NULL for the playback or capture lists in which case they'll be ignored. 8403 8404 The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. 8405 8406 8407 See Also 8408 -------- 8409 ma_context_get_devices() 8410 */ 8411 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); 8412 8413 /* 8414 Retrieves information about a device of the given type, with the specified ID and share mode. 8415 8416 8417 Parameters 8418 ---------- 8419 pContext (in) 8420 A pointer to the context performing the query. 8421 8422 deviceType (in) 8423 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. 8424 8425 pDeviceID (in) 8426 The ID of the device being queried. 8427 8428 pDeviceInfo (out) 8429 A pointer to the `ma_device_info` structure that will receive the device information. 8430 8431 8432 Return Value 8433 ------------ 8434 MA_SUCCESS if successful; any other error code otherwise. 8435 8436 8437 Thread Safety 8438 ------------- 8439 Safe. This is guarded using a simple mutex lock. 8440 8441 8442 Remarks 8443 ------- 8444 Do _not_ call this from within the `ma_context_enumerate_devices()` callback. 8445 8446 It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in 8447 shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify 8448 which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if 8449 the requested share mode is unsupported. 8450 8451 This leaves pDeviceInfo unmodified in the result of an error. 8452 */ 8453 MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); 8454 8455 /* 8456 Determines if the given context supports loopback mode. 8457 8458 8459 Parameters 8460 ---------- 8461 pContext (in) 8462 A pointer to the context getting queried. 8463 8464 8465 Return Value 8466 ------------ 8467 MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. 8468 */ 8469 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); 8470 8471 8472 8473 /* 8474 Initializes a device config with default settings. 8475 8476 8477 Parameters 8478 ---------- 8479 deviceType (in) 8480 The type of the device this config is being initialized for. This must set to one of the following: 8481 8482 |-------------------------| 8483 | Device Type | 8484 |-------------------------| 8485 | ma_device_type_playback | 8486 | ma_device_type_capture | 8487 | ma_device_type_duplex | 8488 | ma_device_type_loopback | 8489 |-------------------------| 8490 8491 8492 Return Value 8493 ------------ 8494 A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. 8495 8496 8497 Thread Safety 8498 ------------- 8499 Safe. 8500 8501 8502 Callback Safety 8503 --------------- 8504 Safe, but don't try initializing a device in a callback. 8505 8506 8507 Remarks 8508 ------- 8509 The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a 8510 typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change 8511 before initializing the device. 8512 8513 See `ma_device_init()` for details on specific configuration options. 8514 8515 8516 Example 1 - Simple Configuration 8517 -------------------------------- 8518 The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and 8519 then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added 8520 to the `ma_device_config` structure. 8521 8522 ```c 8523 ma_device_config config = ma_device_config_init(ma_device_type_playback); 8524 config.playback.format = ma_format_f32; 8525 config.playback.channels = 2; 8526 config.sampleRate = 48000; 8527 config.dataCallback = ma_data_callback; 8528 config.pUserData = pMyUserData; 8529 ``` 8530 8531 8532 See Also 8533 -------- 8534 ma_device_init() 8535 ma_device_init_ex() 8536 */ 8537 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); 8538 8539 8540 /* 8541 Initializes a device. 8542 8543 A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it 8544 from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be 8545 playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the 8546 device is done via a callback which is fired by miniaudio at periodic time intervals. 8547 8548 The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames 8549 or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and 8550 increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but 8551 miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple 8552 media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the 8553 backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. 8554 8555 When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the 8556 format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you 8557 can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. 8558 8559 8560 Parameters 8561 ---------- 8562 pContext (in, optional) 8563 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. 8564 8565 pConfig (in) 8566 A pointer to the device configuration. Cannot be null. See remarks for details. 8567 8568 pDevice (out) 8569 A pointer to the device object being initialized. 8570 8571 8572 Return Value 8573 ------------ 8574 MA_SUCCESS if successful; any other error code otherwise. 8575 8576 8577 Thread Safety 8578 ------------- 8579 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to 8580 calling this at the same time as `ma_device_uninit()`. 8581 8582 8583 Callback Safety 8584 --------------- 8585 Unsafe. It is not safe to call this inside any callback. 8586 8587 8588 Remarks 8589 ------- 8590 Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: 8591 8592 ```c 8593 ma_context_init(NULL, 0, NULL, &context); 8594 ``` 8595 8596 Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use 8597 device.pContext for the initialization of other devices. 8598 8599 The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can 8600 then be set directly on the structure. Below are the members of the `ma_device_config` object. 8601 8602 deviceType 8603 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. 8604 8605 sampleRate 8606 The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. 8607 8608 periodSizeInFrames 8609 The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will 8610 be used depending on the selected performance profile. This value affects latency. See below for details. 8611 8612 periodSizeInMilliseconds 8613 The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be 8614 used depending on the selected performance profile. The value affects latency. See below for details. 8615 8616 periods 8617 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by 8618 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. 8619 8620 performanceProfile 8621 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or 8622 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. 8623 8624 noPreSilencedOutputBuffer 8625 When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of 8626 the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data 8627 callback will write to every sample in the output buffer, or if you are doing your own clearing. 8628 8629 noClip 8630 When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or 8631 not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only 8632 applies when the playback sample format is f32. 8633 8634 noDisableDenormals 8635 By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. 8636 8637 noFixedSizedCallback 8638 Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a 8639 consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with 8640 whatever the backend requests, which could be anything. 8641 8642 dataCallback 8643 The callback to fire whenever data is ready to be delivered to or from the device. 8644 8645 notificationCallback 8646 The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. 8647 8648 pUserData 8649 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. 8650 8651 resampling.algorithm 8652 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The 8653 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. 8654 8655 resampling.pBackendVTable 8656 A pointer to an optional vtable that can be used for plugging in a custom resampler. 8657 8658 resampling.pBackendUserData 8659 A pointer that will passed to callbacks in pBackendVTable. 8660 8661 resampling.linear.lpfOrder 8662 The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher 8663 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is 8664 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. 8665 8666 playback.pDeviceID 8667 A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's 8668 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. 8669 8670 playback.format 8671 The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after 8672 initialization from the device object directly with `device.playback.format`. 8673 8674 playback.channels 8675 The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization 8676 from the device object directly with `device.playback.channels`. 8677 8678 playback.pChannelMap 8679 The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the 8680 device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. 8681 8682 playback.shareMode 8683 The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify 8684 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to 8685 ma_share_mode_shared and reinitializing. 8686 8687 capture.pDeviceID 8688 A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's 8689 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. 8690 8691 capture.format 8692 The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after 8693 initialization from the device object directly with `device.capture.format`. 8694 8695 capture.channels 8696 The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization 8697 from the device object directly with `device.capture.channels`. 8698 8699 capture.pChannelMap 8700 The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the 8701 device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. 8702 8703 capture.shareMode 8704 The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify 8705 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to 8706 ma_share_mode_shared and reinitializing. 8707 8708 wasapi.noAutoConvertSRC 8709 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. 8710 8711 wasapi.noDefaultQualitySRC 8712 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. 8713 You should usually leave this set to false, which is the default. 8714 8715 wasapi.noAutoStreamRouting 8716 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. 8717 8718 wasapi.noHardwareOffloading 8719 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. 8720 8721 alsa.noMMap 8722 ALSA only. When set to true, disables MMap mode. Defaults to false. 8723 8724 alsa.noAutoFormat 8725 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. 8726 8727 alsa.noAutoChannels 8728 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. 8729 8730 alsa.noAutoResample 8731 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. 8732 8733 pulse.pStreamNamePlayback 8734 PulseAudio only. Sets the stream name for playback. 8735 8736 pulse.pStreamNameCapture 8737 PulseAudio only. Sets the stream name for capture. 8738 8739 coreaudio.allowNominalSampleRateChange 8740 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This 8741 is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate 8742 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will 8743 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the 8744 hardware. When set to false, the sample rate currently set by the operating system will always be used. 8745 8746 opensl.streamType 8747 OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the 8748 stream type will be left unset. Think of this as the type of audio you're playing. 8749 8750 opensl.recordingPreset 8751 OpenSL only. Explicitly sets the type of recording your program will be doing. When left 8752 unset, the recording preset will be left unchanged. 8753 8754 aaudio.usage 8755 AAudio only. Explicitly sets the nature of the audio the program will be consuming. When 8756 left unset, the usage will be left unchanged. 8757 8758 aaudio.contentType 8759 AAudio only. Sets the content type. When left unset, the content type will be left unchanged. 8760 8761 aaudio.inputPreset 8762 AAudio only. Explicitly sets the type of recording your program will be doing. When left 8763 unset, the input preset will be left unchanged. 8764 8765 aaudio.noAutoStartAfterReroute 8766 AAudio only. Controls whether or not the device should be automatically restarted after a 8767 stream reroute. When set to false (default) the device will be restarted automatically; 8768 otherwise the device will be stopped. 8769 8770 8771 Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. 8772 8773 After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. 8774 8775 If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or 8776 `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or 8777 `ma_performance_profile_conservative`. 8778 8779 If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device 8780 in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the 8781 config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, 8782 for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. 8783 Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. 8784 8785 When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config 8786 and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run 8787 on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, 8788 `playback/capture.channels` and `sampleRate` members of the device object. 8789 8790 When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message 8791 asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. 8792 8793 ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. 8794 If these fail it will try falling back to the "hw" device. 8795 8796 8797 Example 1 - Simple Initialization 8798 --------------------------------- 8799 This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default 8800 playback device this is usually all you need. 8801 8802 ```c 8803 ma_device_config config = ma_device_config_init(ma_device_type_playback); 8804 config.playback.format = ma_format_f32; 8805 config.playback.channels = 2; 8806 config.sampleRate = 48000; 8807 config.dataCallback = ma_data_callback; 8808 config.pMyUserData = pMyUserData; 8809 8810 ma_device device; 8811 ma_result result = ma_device_init(NULL, &config, &device); 8812 if (result != MA_SUCCESS) { 8813 // Error 8814 } 8815 ``` 8816 8817 8818 Example 2 - Advanced Initialization 8819 ----------------------------------- 8820 This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size 8821 and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device 8822 enumeration. 8823 8824 ```c 8825 ma_context context; 8826 ma_result result = ma_context_init(NULL, 0, NULL, &context); 8827 if (result != MA_SUCCESS) { 8828 // Error 8829 } 8830 8831 ma_device_info* pPlaybackDeviceInfos; 8832 ma_uint32 playbackDeviceCount; 8833 result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); 8834 if (result != MA_SUCCESS) { 8835 // Error 8836 } 8837 8838 // ... choose a device from pPlaybackDeviceInfos ... 8839 8840 ma_device_config config = ma_device_config_init(ma_device_type_playback); 8841 config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). 8842 config.playback.format = ma_format_f32; 8843 config.playback.channels = 2; 8844 config.sampleRate = 48000; 8845 config.dataCallback = ma_data_callback; 8846 config.pUserData = pMyUserData; 8847 config.periodSizeInMilliseconds = 10; 8848 config.periods = 3; 8849 8850 ma_device device; 8851 result = ma_device_init(&context, &config, &device); 8852 if (result != MA_SUCCESS) { 8853 // Error 8854 } 8855 ``` 8856 8857 8858 See Also 8859 -------- 8860 ma_device_config_init() 8861 ma_device_uninit() 8862 ma_device_start() 8863 ma_context_init() 8864 ma_context_get_devices() 8865 ma_context_enumerate_devices() 8866 */ 8867 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); 8868 8869 /* 8870 Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. 8871 8872 This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function 8873 allows you to configure the internally created context. 8874 8875 8876 Parameters 8877 ---------- 8878 backends (in, optional) 8879 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. 8880 8881 backendCount (in, optional) 8882 The number of items in `backend`. Ignored if `backend` is NULL. 8883 8884 pContextConfig (in, optional) 8885 The context configuration. 8886 8887 pConfig (in) 8888 A pointer to the device configuration. Cannot be null. See remarks for details. 8889 8890 pDevice (out) 8891 A pointer to the device object being initialized. 8892 8893 8894 Return Value 8895 ------------ 8896 MA_SUCCESS if successful; any other error code otherwise. 8897 8898 8899 Thread Safety 8900 ------------- 8901 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to 8902 calling this at the same time as `ma_device_uninit()`. 8903 8904 8905 Callback Safety 8906 --------------- 8907 Unsafe. It is not safe to call this inside any callback. 8908 8909 8910 Remarks 8911 ------- 8912 You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage 8913 your own context. 8914 8915 See the documentation for `ma_context_init()` for information on the different context configuration options. 8916 8917 8918 See Also 8919 -------- 8920 ma_device_init() 8921 ma_device_uninit() 8922 ma_device_config_init() 8923 ma_context_init() 8924 */ 8925 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); 8926 8927 /* 8928 Uninitializes a device. 8929 8930 This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. 8931 8932 8933 Parameters 8934 ---------- 8935 pDevice (in) 8936 A pointer to the device to stop. 8937 8938 8939 Return Value 8940 ------------ 8941 Nothing 8942 8943 8944 Thread Safety 8945 ------------- 8946 Unsafe. As soon as this API is called the device should be considered undefined. 8947 8948 8949 Callback Safety 8950 --------------- 8951 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. 8952 8953 8954 See Also 8955 -------- 8956 ma_device_init() 8957 ma_device_stop() 8958 */ 8959 MA_API void ma_device_uninit(ma_device* pDevice); 8960 8961 8962 /* 8963 Retrieves a pointer to the context that owns the given device. 8964 */ 8965 MA_API ma_context* ma_device_get_context(ma_device* pDevice); 8966 8967 /* 8968 Helper function for retrieving the log object associated with the context that owns this device. 8969 */ 8970 MA_API ma_log* ma_device_get_log(ma_device* pDevice); 8971 8972 8973 /* 8974 Retrieves information about the device. 8975 8976 8977 Parameters 8978 ---------- 8979 pDevice (in) 8980 A pointer to the device whose information is being retrieved. 8981 8982 type (in) 8983 The device type. This parameter is required for duplex devices. When retrieving device 8984 information, you are doing so for an individual playback or capture device. 8985 8986 pDeviceInfo (out) 8987 A pointer to the `ma_device_info` that will receive the device information. 8988 8989 8990 Return Value 8991 ------------ 8992 MA_SUCCESS if successful; any other error code otherwise. 8993 8994 8995 Thread Safety 8996 ------------- 8997 Unsafe. This should be considered unsafe because it may be calling into the backend which may or 8998 may not be safe. 8999 9000 9001 Callback Safety 9002 --------------- 9003 Unsafe. You should avoid calling this in the data callback because it may call into the backend 9004 which may or may not be safe. 9005 */ 9006 MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); 9007 9008 9009 /* 9010 Retrieves the name of the device. 9011 9012 9013 Parameters 9014 ---------- 9015 pDevice (in) 9016 A pointer to the device whose information is being retrieved. 9017 9018 type (in) 9019 The device type. This parameter is required for duplex devices. When retrieving device 9020 information, you are doing so for an individual playback or capture device. 9021 9022 pName (out) 9023 A pointer to the buffer that will receive the name. 9024 9025 nameCap (in) 9026 The capacity of the output buffer, including space for the null terminator. 9027 9028 pLengthNotIncludingNullTerminator (out, optional) 9029 A pointer to the variable that will receive the length of the name, not including the null 9030 terminator. 9031 9032 9033 Return Value 9034 ------------ 9035 MA_SUCCESS if successful; any other error code otherwise. 9036 9037 9038 Thread Safety 9039 ------------- 9040 Unsafe. This should be considered unsafe because it may be calling into the backend which may or 9041 may not be safe. 9042 9043 9044 Callback Safety 9045 --------------- 9046 Unsafe. You should avoid calling this in the data callback because it may call into the backend 9047 which may or may not be safe. 9048 9049 9050 Remarks 9051 ------- 9052 If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to 9053 `pName` if you want to first get the length of the name for the purpose of memory allocation of the 9054 output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for 9055 most cases and will avoid the need for the inefficiency of calling this function twice. 9056 9057 This is implemented in terms of `ma_device_get_info()`. 9058 */ 9059 MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); 9060 9061 9062 /* 9063 Starts the device. For playback devices this begins playback. For capture devices it begins recording. 9064 9065 Use `ma_device_stop()` to stop the device. 9066 9067 9068 Parameters 9069 ---------- 9070 pDevice (in) 9071 A pointer to the device to start. 9072 9073 9074 Return Value 9075 ------------ 9076 MA_SUCCESS if successful; any other error code otherwise. 9077 9078 9079 Thread Safety 9080 ------------- 9081 Safe. It's safe to call this from any thread with the exception of the callback thread. 9082 9083 9084 Callback Safety 9085 --------------- 9086 Unsafe. It is not safe to call this inside any callback. 9087 9088 9089 Remarks 9090 ------- 9091 For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid 9092 audio data in the buffer, which needs to be done before the device begins playback. 9093 9094 This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. 9095 9096 Do not call this in any callback. 9097 9098 9099 See Also 9100 -------- 9101 ma_device_stop() 9102 */ 9103 MA_API ma_result ma_device_start(ma_device* pDevice); 9104 9105 /* 9106 Stops the device. For playback devices this stops playback. For capture devices it stops recording. 9107 9108 Use `ma_device_start()` to start the device again. 9109 9110 9111 Parameters 9112 ---------- 9113 pDevice (in) 9114 A pointer to the device to stop. 9115 9116 9117 Return Value 9118 ------------ 9119 MA_SUCCESS if successful; any other error code otherwise. 9120 9121 9122 Thread Safety 9123 ------------- 9124 Safe. It's safe to call this from any thread with the exception of the callback thread. 9125 9126 9127 Callback Safety 9128 --------------- 9129 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. 9130 9131 9132 Remarks 9133 ------- 9134 This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some 9135 backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size 9136 that was specified at initialization time). 9137 9138 Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and 9139 the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the 9140 speakers or received from the microphone which can in turn result in de-syncs. 9141 9142 Do not call this in any callback. 9143 9144 9145 See Also 9146 -------- 9147 ma_device_start() 9148 */ 9149 MA_API ma_result ma_device_stop(ma_device* pDevice); 9150 9151 /* 9152 Determines whether or not the device is started. 9153 9154 9155 Parameters 9156 ---------- 9157 pDevice (in) 9158 A pointer to the device whose start state is being retrieved. 9159 9160 9161 Return Value 9162 ------------ 9163 True if the device is started, false otherwise. 9164 9165 9166 Thread Safety 9167 ------------- 9168 Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return 9169 value will be out of sync. 9170 9171 9172 Callback Safety 9173 --------------- 9174 Safe. This is implemented as a simple accessor. 9175 9176 9177 See Also 9178 -------- 9179 ma_device_start() 9180 ma_device_stop() 9181 */ 9182 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); 9183 9184 9185 /* 9186 Retrieves the state of the device. 9187 9188 9189 Parameters 9190 ---------- 9191 pDevice (in) 9192 A pointer to the device whose state is being retrieved. 9193 9194 9195 Return Value 9196 ------------ 9197 The current state of the device. The return value will be one of the following: 9198 9199 +-------------------------------+------------------------------------------------------------------------------+ 9200 | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | 9201 +-------------------------------+------------------------------------------------------------------------------+ 9202 | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | 9203 +-------------------------------+------------------------------------------------------------------------------+ 9204 | ma_device_state_started | The device started and requesting and/or delivering audio data. | 9205 +-------------------------------+------------------------------------------------------------------------------+ 9206 | ma_device_state_starting | The device is in the process of starting. | 9207 +-------------------------------+------------------------------------------------------------------------------+ 9208 | ma_device_state_stopping | The device is in the process of stopping. | 9209 +-------------------------------+------------------------------------------------------------------------------+ 9210 9211 9212 Thread Safety 9213 ------------- 9214 Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, 9215 there's a possibility the return value could be out of sync. See remarks. 9216 9217 9218 Callback Safety 9219 --------------- 9220 Safe. This is implemented as a simple accessor. 9221 9222 9223 Remarks 9224 ------- 9225 The general flow of a devices state goes like this: 9226 9227 ``` 9228 ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped 9229 ma_device_start() -> ma_device_state_starting -> ma_device_state_started 9230 ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped 9231 ``` 9232 9233 When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the 9234 value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own 9235 synchronization. 9236 */ 9237 MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); 9238 9239 9240 /* 9241 Performs post backend initialization routines for setting up internal data conversion. 9242 9243 This should be called whenever the backend is initialized. The only time this should be called from 9244 outside of miniaudio is if you're implementing a custom backend, and you would only do it if you 9245 are reinitializing the backend due to rerouting or reinitializing for some reason. 9246 9247 9248 Parameters 9249 ---------- 9250 pDevice [in] 9251 A pointer to the device. 9252 9253 deviceType [in] 9254 The type of the device that was just reinitialized. 9255 9256 pPlaybackDescriptor [in] 9257 The descriptor of the playback device containing the internal data format and buffer sizes. 9258 9259 pPlaybackDescriptor [in] 9260 The descriptor of the capture device containing the internal data format and buffer sizes. 9261 9262 9263 Return Value 9264 ------------ 9265 MA_SUCCESS if successful; any other error otherwise. 9266 9267 9268 Thread Safety 9269 ------------- 9270 Unsafe. This will be reinitializing internal data converters which may be in use by another thread. 9271 9272 9273 Callback Safety 9274 --------------- 9275 Unsafe. This will be reinitializing internal data converters which may be in use by the callback. 9276 9277 9278 Remarks 9279 ------- 9280 For a duplex device, you can call this for only one side of the system. This is why the deviceType 9281 is specified as a parameter rather than deriving it from the device. 9282 9283 You do not need to call this manually unless you are doing a custom backend, in which case you need 9284 only do it if you're manually performing rerouting or reinitialization. 9285 */ 9286 MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); 9287 9288 9289 /* 9290 Sets the master volume factor for the device. 9291 9292 The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and 9293 values less than 0 decreases the volume. 9294 9295 9296 Parameters 9297 ---------- 9298 pDevice (in) 9299 A pointer to the device whose volume is being set. 9300 9301 volume (in) 9302 The new volume factor. Must be >= 0. 9303 9304 9305 Return Value 9306 ------------ 9307 MA_SUCCESS if the volume was set successfully. 9308 MA_INVALID_ARGS if pDevice is NULL. 9309 MA_INVALID_ARGS if volume is negative. 9310 9311 9312 Thread Safety 9313 ------------- 9314 Safe. This just sets a local member of the device object. 9315 9316 9317 Callback Safety 9318 --------------- 9319 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. 9320 9321 9322 Remarks 9323 ------- 9324 This applies the volume factor across all channels. 9325 9326 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. 9327 9328 9329 See Also 9330 -------- 9331 ma_device_get_master_volume() 9332 ma_device_set_master_volume_db() 9333 ma_device_get_master_volume_db() 9334 */ 9335 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); 9336 9337 /* 9338 Retrieves the master volume factor for the device. 9339 9340 9341 Parameters 9342 ---------- 9343 pDevice (in) 9344 A pointer to the device whose volume factor is being retrieved. 9345 9346 pVolume (in) 9347 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. 9348 9349 9350 Return Value 9351 ------------ 9352 MA_SUCCESS if successful. 9353 MA_INVALID_ARGS if pDevice is NULL. 9354 MA_INVALID_ARGS if pVolume is NULL. 9355 9356 9357 Thread Safety 9358 ------------- 9359 Safe. This just a simple member retrieval. 9360 9361 9362 Callback Safety 9363 --------------- 9364 Safe. 9365 9366 9367 Remarks 9368 ------- 9369 If an error occurs, `*pVolume` will be set to 0. 9370 9371 9372 See Also 9373 -------- 9374 ma_device_set_master_volume() 9375 ma_device_set_master_volume_gain_db() 9376 ma_device_get_master_volume_gain_db() 9377 */ 9378 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); 9379 9380 /* 9381 Sets the master volume for the device as gain in decibels. 9382 9383 A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. 9384 9385 9386 Parameters 9387 ---------- 9388 pDevice (in) 9389 A pointer to the device whose gain is being set. 9390 9391 gainDB (in) 9392 The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. 9393 9394 9395 Return Value 9396 ------------ 9397 MA_SUCCESS if the volume was set successfully. 9398 MA_INVALID_ARGS if pDevice is NULL. 9399 MA_INVALID_ARGS if the gain is > 0. 9400 9401 9402 Thread Safety 9403 ------------- 9404 Safe. This just sets a local member of the device object. 9405 9406 9407 Callback Safety 9408 --------------- 9409 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. 9410 9411 9412 Remarks 9413 ------- 9414 This applies the gain across all channels. 9415 9416 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. 9417 9418 9419 See Also 9420 -------- 9421 ma_device_get_master_volume_gain_db() 9422 ma_device_set_master_volume() 9423 ma_device_get_master_volume() 9424 */ 9425 MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); 9426 9427 /* 9428 Retrieves the master gain in decibels. 9429 9430 9431 Parameters 9432 ---------- 9433 pDevice (in) 9434 A pointer to the device whose gain is being retrieved. 9435 9436 pGainDB (in) 9437 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. 9438 9439 9440 Return Value 9441 ------------ 9442 MA_SUCCESS if successful. 9443 MA_INVALID_ARGS if pDevice is NULL. 9444 MA_INVALID_ARGS if pGainDB is NULL. 9445 9446 9447 Thread Safety 9448 ------------- 9449 Safe. This just a simple member retrieval. 9450 9451 9452 Callback Safety 9453 --------------- 9454 Safe. 9455 9456 9457 Remarks 9458 ------- 9459 If an error occurs, `*pGainDB` will be set to 0. 9460 9461 9462 See Also 9463 -------- 9464 ma_device_set_master_volume_db() 9465 ma_device_set_master_volume() 9466 ma_device_get_master_volume() 9467 */ 9468 MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); 9469 9470 9471 /* 9472 Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. 9473 9474 9475 Parameters 9476 ---------- 9477 pDevice (in) 9478 A pointer to device whose processing the data callback. 9479 9480 pOutput (out) 9481 A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device 9482 this can be NULL, in which case pInput must not be NULL. 9483 9484 pInput (in) 9485 A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be 9486 NULL, in which case `pOutput` must not be NULL. 9487 9488 frameCount (in) 9489 The number of frames being processed. 9490 9491 9492 Return Value 9493 ------------ 9494 MA_SUCCESS if successful; any other result code otherwise. 9495 9496 9497 Thread Safety 9498 ------------- 9499 This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a 9500 playback and capture device in duplex setups. 9501 9502 9503 Callback Safety 9504 --------------- 9505 Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. 9506 9507 9508 Remarks 9509 ------- 9510 If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in 9511 which case `pInput` will be processed first, followed by `pOutput`. 9512 9513 If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that 9514 callback. 9515 */ 9516 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); 9517 9518 9519 /* 9520 Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. 9521 9522 This function is used by backends for helping determine an appropriately sized buffer to use with 9523 the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the 9524 `pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a 9525 best guess at the device's native sample rate is also required which is where `nativeSampleRate` 9526 comes in. In addition, the performance profile is also needed for cases where both the period size 9527 in frames and milliseconds are both zero. 9528 9529 9530 Parameters 9531 ---------- 9532 pDescriptor (in) 9533 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members 9534 will be used for the calculation of the buffer size. 9535 9536 nativeSampleRate (in) 9537 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of 9538 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which 9539 case a sample rate is required to convert to a size in frames. 9540 9541 performanceProfile (in) 9542 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are 9543 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile 9544 to use for this calculation is determine by this parameter. 9545 9546 9547 Return Value 9548 ------------ 9549 The calculated buffer size in frames. 9550 9551 9552 Thread Safety 9553 ------------- 9554 This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function 9555 should only ever be called from within the backend's device initialization routine and therefore 9556 shouldn't have any multithreading concerns. 9557 9558 9559 Callback Safety 9560 --------------- 9561 This is safe to call within the data callback, but there is no reason to ever do this. 9562 9563 9564 Remarks 9565 ------- 9566 If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that 9567 is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. 9568 */ 9569 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); 9570 9571 9572 9573 /* 9574 Retrieves a friendly name for a backend. 9575 */ 9576 MA_API const char* ma_get_backend_name(ma_backend backend); 9577 9578 /* 9579 Retrieves the backend enum from the given name. 9580 */ 9581 MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); 9582 9583 /* 9584 Determines whether or not the given backend is available by the compilation environment. 9585 */ 9586 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); 9587 9588 /* 9589 Retrieves compile-time enabled backends. 9590 9591 9592 Parameters 9593 ---------- 9594 pBackends (out, optional) 9595 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting 9596 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. 9597 9598 backendCap (in) 9599 The capacity of the `pBackends` buffer. 9600 9601 pBackendCount (out) 9602 A pointer to the variable that will receive the enabled backend count. 9603 9604 9605 Return Value 9606 ------------ 9607 MA_SUCCESS if successful. 9608 MA_INVALID_ARGS if `pBackendCount` is NULL. 9609 MA_NO_SPACE if the capacity of `pBackends` is not large enough. 9610 9611 If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. 9612 9613 9614 Thread Safety 9615 ------------- 9616 Safe. 9617 9618 9619 Callback Safety 9620 --------------- 9621 Safe. 9622 9623 9624 Remarks 9625 ------- 9626 If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call 9627 this function with `pBackends` set to NULL. 9628 9629 This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` 9630 when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at 9631 compile time with `MA_NO_NULL`. 9632 9633 The returned backends are determined based on compile time settings, not the platform it's currently running on. For 9634 example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have 9635 PulseAudio installed. 9636 9637 9638 Example 1 9639 --------- 9640 The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is 9641 given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. 9642 Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. 9643 9644 ``` 9645 ma_backend enabledBackends[MA_BACKEND_COUNT]; 9646 size_t enabledBackendCount; 9647 9648 result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); 9649 if (result != MA_SUCCESS) { 9650 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. 9651 } 9652 ``` 9653 9654 9655 See Also 9656 -------- 9657 ma_is_backend_enabled() 9658 */ 9659 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); 9660 9661 /* 9662 Determines whether or not loopback mode is support by a backend. 9663 */ 9664 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); 9665 9666 #endif /* MA_NO_DEVICE_IO */ 9667 9668 9669 9670 /************************************************************************************************************************************************************ 9671 9672 Utilities 9673 9674 ************************************************************************************************************************************************************/ 9675 9676 /* 9677 Calculates a buffer size in milliseconds from the specified number of frames and sample rate. 9678 */ 9679 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); 9680 9681 /* 9682 Calculates a buffer size in frames from the specified number of milliseconds and sample rate. 9683 */ 9684 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); 9685 9686 /* 9687 Copies PCM frames from one buffer to another. 9688 */ 9689 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); 9690 9691 /* 9692 Copies silent frames into the given buffer. 9693 9694 Remarks 9695 ------- 9696 For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it 9697 makes more sense for the purpose of mixing to initialize it to the center point. 9698 */ 9699 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); 9700 9701 9702 /* 9703 Offsets a pointer by the specified number of PCM frames. 9704 */ 9705 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); 9706 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); 9707 static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } 9708 static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } 9709 9710 9711 /* 9712 Clips samples. 9713 */ 9714 MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); 9715 MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); 9716 MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); 9717 MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); 9718 MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); 9719 MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); 9720 9721 /* 9722 Helper for applying a volume factor to samples. 9723 9724 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. 9725 */ 9726 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); 9727 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); 9728 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); 9729 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); 9730 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); 9731 9732 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); 9733 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); 9734 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); 9735 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); 9736 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); 9737 9738 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); 9739 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); 9740 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); 9741 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); 9742 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); 9743 MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); 9744 9745 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); 9746 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); 9747 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); 9748 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); 9749 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); 9750 MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); 9751 9752 MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); 9753 9754 9755 MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); 9756 MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); 9757 MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); 9758 MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); 9759 MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); 9760 MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); 9761 9762 9763 /* 9764 Helper for converting a linear factor to gain in decibels. 9765 */ 9766 MA_API float ma_volume_linear_to_db(float factor); 9767 9768 /* 9769 Helper for converting gain in decibels to a linear factor. 9770 */ 9771 MA_API float ma_volume_db_to_linear(float gain); 9772 9773 9774 /* 9775 Mixes the specified number of frames in floating point format with a volume factor. 9776 9777 This will run on an optimized path when the volume is equal to 1. 9778 */ 9779 MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); 9780 9781 9782 9783 9784 /************************************************************************************************************************************************************ 9785 9786 VFS 9787 === 9788 9789 The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely 9790 appropriate for a given situation. 9791 9792 ************************************************************************************************************************************************************/ 9793 typedef void ma_vfs; 9794 typedef ma_handle ma_vfs_file; 9795 9796 typedef enum 9797 { 9798 MA_OPEN_MODE_READ = 0x00000001, 9799 MA_OPEN_MODE_WRITE = 0x00000002 9800 } ma_open_mode_flags; 9801 9802 typedef enum 9803 { 9804 ma_seek_origin_start, 9805 ma_seek_origin_current, 9806 ma_seek_origin_end /* Not used by decoders. */ 9807 } ma_seek_origin; 9808 9809 typedef struct 9810 { 9811 ma_uint64 sizeInBytes; 9812 } ma_file_info; 9813 9814 typedef struct 9815 { 9816 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); 9817 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); 9818 ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); 9819 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); 9820 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); 9821 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); 9822 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); 9823 ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); 9824 } ma_vfs_callbacks; 9825 9826 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); 9827 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); 9828 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); 9829 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); 9830 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); 9831 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); 9832 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); 9833 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); 9834 MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); 9835 9836 typedef struct 9837 { 9838 ma_vfs_callbacks cb; 9839 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ 9840 } ma_default_vfs; 9841 9842 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); 9843 9844 9845 9846 typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); 9847 typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); 9848 typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); 9849 9850 9851 9852 #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) 9853 typedef enum 9854 { 9855 ma_encoding_format_unknown = 0, 9856 ma_encoding_format_wav, 9857 ma_encoding_format_flac, 9858 ma_encoding_format_mp3, 9859 ma_encoding_format_vorbis 9860 } ma_encoding_format; 9861 #endif 9862 9863 /************************************************************************************************************************************************************ 9864 9865 Decoding 9866 ======== 9867 9868 Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless 9869 you do your own synchronization. 9870 9871 ************************************************************************************************************************************************************/ 9872 #ifndef MA_NO_DECODING 9873 typedef struct ma_decoder ma_decoder; 9874 9875 9876 typedef struct 9877 { 9878 ma_format preferredFormat; 9879 ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ 9880 } ma_decoding_backend_config; 9881 9882 MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); 9883 9884 9885 typedef struct 9886 { 9887 ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); 9888 ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ 9889 ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ 9890 ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ 9891 void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); 9892 } ma_decoding_backend_vtable; 9893 9894 9895 typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ 9896 typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); 9897 typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); 9898 9899 typedef struct 9900 { 9901 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ 9902 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ 9903 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ 9904 ma_channel* pChannelMap; 9905 ma_channel_mix_mode channelMixMode; 9906 ma_dither_mode ditherMode; 9907 ma_resampler_config resampling; 9908 ma_allocation_callbacks allocationCallbacks; 9909 ma_encoding_format encodingFormat; 9910 ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ 9911 ma_decoding_backend_vtable** ppCustomBackendVTables; 9912 ma_uint32 customBackendCount; 9913 void* pCustomBackendUserData; 9914 } ma_decoder_config; 9915 9916 struct ma_decoder 9917 { 9918 ma_data_source_base ds; 9919 ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ 9920 const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ 9921 void* pBackendUserData; 9922 ma_decoder_read_proc onRead; 9923 ma_decoder_seek_proc onSeek; 9924 ma_decoder_tell_proc onTell; 9925 void* pUserData; 9926 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ 9927 ma_format outputFormat; 9928 ma_uint32 outputChannels; 9929 ma_uint32 outputSampleRate; 9930 ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ 9931 void* pInputCache; /* In input format. Can be null if it's not needed. */ 9932 ma_uint64 inputCacheCap; /* The capacity of the input cache. */ 9933 ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ 9934 ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ 9935 ma_allocation_callbacks allocationCallbacks; 9936 union 9937 { 9938 struct 9939 { 9940 ma_vfs* pVFS; 9941 ma_vfs_file file; 9942 } vfs; 9943 struct 9944 { 9945 const ma_uint8* pData; 9946 size_t dataSize; 9947 size_t currentReadPos; 9948 } memory; /* Only used for decoders that were opened against a block of memory. */ 9949 } data; 9950 }; 9951 9952 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); 9953 MA_API ma_decoder_config ma_decoder_config_init_default(void); 9954 9955 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9956 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9957 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9958 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9959 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9960 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); 9961 9962 /* 9963 Uninitializes a decoder. 9964 */ 9965 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); 9966 9967 /* 9968 Reads PCM frames from the given decoder. 9969 9970 This is not thread safe without your own synchronization. 9971 */ 9972 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 9973 9974 /* 9975 Seeks to a PCM frame based on it's absolute index. 9976 9977 This is not thread safe without your own synchronization. 9978 */ 9979 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); 9980 9981 /* 9982 Retrieves the decoder's output data format. 9983 */ 9984 MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 9985 9986 /* 9987 Retrieves the current position of the read cursor in PCM frames. 9988 */ 9989 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); 9990 9991 /* 9992 Retrieves the length of the decoder in PCM frames. 9993 9994 Do not call this on streams of an undefined length, such as internet radio. 9995 9996 If the length is unknown or an error occurs, 0 will be returned. 9997 9998 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio 9999 uses internally. 10000 10001 For MP3's, this will decode the entire file. Do not call this in time critical scenarios. 10002 10003 This function is not thread safe without your own synchronization. 10004 */ 10005 MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); 10006 10007 /* 10008 Retrieves the number of frames that can be read before reaching the end. 10009 10010 This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in 10011 particular ensuring you do not call it on streams of an undefined length, such as internet radio. 10012 10013 If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be 10014 returned. 10015 */ 10016 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); 10017 10018 /* 10019 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, 10020 pConfig should be set to what you want. On output it will be set to what you got. 10021 */ 10022 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); 10023 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); 10024 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); 10025 10026 #endif /* MA_NO_DECODING */ 10027 10028 10029 /************************************************************************************************************************************************************ 10030 10031 Encoding 10032 ======== 10033 10034 Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. 10035 10036 ************************************************************************************************************************************************************/ 10037 #ifndef MA_NO_ENCODING 10038 typedef struct ma_encoder ma_encoder; 10039 10040 typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); 10041 typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); 10042 typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); 10043 typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); 10044 typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); 10045 10046 typedef struct 10047 { 10048 ma_encoding_format encodingFormat; 10049 ma_format format; 10050 ma_uint32 channels; 10051 ma_uint32 sampleRate; 10052 ma_allocation_callbacks allocationCallbacks; 10053 } ma_encoder_config; 10054 10055 MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); 10056 10057 struct ma_encoder 10058 { 10059 ma_encoder_config config; 10060 ma_encoder_write_proc onWrite; 10061 ma_encoder_seek_proc onSeek; 10062 ma_encoder_init_proc onInit; 10063 ma_encoder_uninit_proc onUninit; 10064 ma_encoder_write_pcm_frames_proc onWritePCMFrames; 10065 void* pUserData; 10066 void* pInternalEncoder; 10067 union 10068 { 10069 struct 10070 { 10071 ma_vfs* pVFS; 10072 ma_vfs_file file; 10073 } vfs; 10074 } data; 10075 }; 10076 10077 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); 10078 MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); 10079 MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); 10080 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); 10081 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); 10082 MA_API void ma_encoder_uninit(ma_encoder* pEncoder); 10083 MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); 10084 10085 #endif /* MA_NO_ENCODING */ 10086 10087 10088 /************************************************************************************************************************************************************ 10089 10090 Generation 10091 10092 ************************************************************************************************************************************************************/ 10093 #ifndef MA_NO_GENERATION 10094 typedef enum 10095 { 10096 ma_waveform_type_sine, 10097 ma_waveform_type_square, 10098 ma_waveform_type_triangle, 10099 ma_waveform_type_sawtooth 10100 } ma_waveform_type; 10101 10102 typedef struct 10103 { 10104 ma_format format; 10105 ma_uint32 channels; 10106 ma_uint32 sampleRate; 10107 ma_waveform_type type; 10108 double amplitude; 10109 double frequency; 10110 } ma_waveform_config; 10111 10112 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); 10113 10114 typedef struct 10115 { 10116 ma_data_source_base ds; 10117 ma_waveform_config config; 10118 double advance; 10119 double time; 10120 } ma_waveform; 10121 10122 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); 10123 MA_API void ma_waveform_uninit(ma_waveform* pWaveform); 10124 MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10125 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); 10126 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); 10127 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); 10128 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); 10129 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); 10130 10131 typedef struct 10132 { 10133 ma_format format; 10134 ma_uint32 channels; 10135 ma_uint32 sampleRate; 10136 double dutyCycle; 10137 double amplitude; 10138 double frequency; 10139 } ma_pulsewave_config; 10140 10141 MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); 10142 10143 typedef struct 10144 { 10145 ma_waveform waveform; 10146 ma_pulsewave_config config; 10147 } ma_pulsewave; 10148 10149 MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); 10150 MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); 10151 MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10152 MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); 10153 MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); 10154 MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); 10155 MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); 10156 MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); 10157 10158 typedef enum 10159 { 10160 ma_noise_type_white, 10161 ma_noise_type_pink, 10162 ma_noise_type_brownian 10163 } ma_noise_type; 10164 10165 10166 typedef struct 10167 { 10168 ma_format format; 10169 ma_uint32 channels; 10170 ma_noise_type type; 10171 ma_int32 seed; 10172 double amplitude; 10173 ma_bool32 duplicateChannels; 10174 } ma_noise_config; 10175 10176 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); 10177 10178 typedef struct 10179 { 10180 ma_data_source_base ds; 10181 ma_noise_config config; 10182 ma_lcg lcg; 10183 union 10184 { 10185 struct 10186 { 10187 double** bin; 10188 double* accumulation; 10189 ma_uint32* counter; 10190 } pink; 10191 struct 10192 { 10193 double* accumulation; 10194 } brownian; 10195 } state; 10196 10197 /* Memory management. */ 10198 void* _pHeap; 10199 ma_bool32 _ownsHeap; 10200 } ma_noise; 10201 10202 MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); 10203 MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); 10204 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); 10205 MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); 10206 MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10207 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); 10208 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); 10209 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); 10210 10211 #endif /* MA_NO_GENERATION */ 10212 10213 10214 10215 /************************************************************************************************************************************************************ 10216 10217 Resource Manager 10218 10219 ************************************************************************************************************************************************************/ 10220 /* The resource manager cannot be enabled if there is no decoder. */ 10221 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) 10222 #define MA_NO_RESOURCE_MANAGER 10223 #endif 10224 10225 #ifndef MA_NO_RESOURCE_MANAGER 10226 typedef struct ma_resource_manager ma_resource_manager; 10227 typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; 10228 typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; 10229 typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; 10230 typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; 10231 10232 typedef enum 10233 { 10234 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ 10235 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ 10236 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ 10237 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ 10238 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ 10239 } ma_resource_manager_data_source_flags; 10240 10241 10242 /* 10243 Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. 10244 */ 10245 typedef struct 10246 { 10247 ma_async_notification* pNotification; 10248 ma_fence* pFence; 10249 } ma_resource_manager_pipeline_stage_notification; 10250 10251 typedef struct 10252 { 10253 ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ 10254 ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ 10255 } ma_resource_manager_pipeline_notifications; 10256 10257 MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); 10258 10259 10260 10261 /* BEGIN BACKWARDS COMPATIBILITY */ 10262 /* TODO: Remove this block in version 0.12. */ 10263 #if 1 10264 #define ma_resource_manager_job ma_job 10265 #define ma_resource_manager_job_init ma_job_init 10266 #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING 10267 #define ma_resource_manager_job_queue_config ma_job_queue_config 10268 #define ma_resource_manager_job_queue_config_init ma_job_queue_config_init 10269 #define ma_resource_manager_job_queue ma_job_queue 10270 #define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size 10271 #define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated 10272 #define ma_resource_manager_job_queue_init ma_job_queue_init 10273 #define ma_resource_manager_job_queue_uninit ma_job_queue_uninit 10274 #define ma_resource_manager_job_queue_post ma_job_queue_post 10275 #define ma_resource_manager_job_queue_next ma_job_queue_next 10276 #endif 10277 /* END BACKWARDS COMPATIBILITY */ 10278 10279 10280 10281 10282 /* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ 10283 #ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 10284 #define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 10285 #endif 10286 10287 typedef enum 10288 { 10289 /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ 10290 MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, 10291 10292 /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ 10293 MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 10294 } ma_resource_manager_flags; 10295 10296 typedef struct 10297 { 10298 const char* pFilePath; 10299 const wchar_t* pFilePathW; 10300 const ma_resource_manager_pipeline_notifications* pNotifications; 10301 ma_uint64 initialSeekPointInPCMFrames; 10302 ma_uint64 rangeBegInPCMFrames; 10303 ma_uint64 rangeEndInPCMFrames; 10304 ma_uint64 loopPointBegInPCMFrames; 10305 ma_uint64 loopPointEndInPCMFrames; 10306 ma_bool32 isLooping; 10307 ma_uint32 flags; 10308 } ma_resource_manager_data_source_config; 10309 10310 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); 10311 10312 10313 typedef enum 10314 { 10315 ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ 10316 ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ 10317 ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ 10318 ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ 10319 } ma_resource_manager_data_supply_type; 10320 10321 typedef struct 10322 { 10323 MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ 10324 union 10325 { 10326 struct 10327 { 10328 const void* pData; 10329 size_t sizeInBytes; 10330 } encoded; 10331 struct 10332 { 10333 const void* pData; 10334 ma_uint64 totalFrameCount; 10335 ma_uint64 decodedFrameCount; 10336 ma_format format; 10337 ma_uint32 channels; 10338 ma_uint32 sampleRate; 10339 } decoded; 10340 struct 10341 { 10342 ma_paged_audio_buffer_data data; 10343 ma_uint64 decodedFrameCount; 10344 ma_uint32 sampleRate; 10345 } decodedPaged; 10346 } backend; 10347 } ma_resource_manager_data_supply; 10348 10349 struct ma_resource_manager_data_buffer_node 10350 { 10351 ma_uint32 hashedName32; /* The hashed name. This is the key. */ 10352 ma_uint32 refCount; 10353 MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ 10354 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ 10355 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ 10356 ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ 10357 ma_resource_manager_data_supply data; 10358 ma_resource_manager_data_buffer_node* pParent; 10359 ma_resource_manager_data_buffer_node* pChildLo; 10360 ma_resource_manager_data_buffer_node* pChildHi; 10361 }; 10362 10363 struct ma_resource_manager_data_buffer 10364 { 10365 ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ 10366 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ 10367 ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ 10368 ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ 10369 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ 10370 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ 10371 ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ 10372 ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ 10373 MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ 10374 MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ 10375 ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ 10376 union 10377 { 10378 ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ 10379 ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ 10380 ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ 10381 } connector; /* Connects this object to the node's data supply. */ 10382 }; 10383 10384 struct ma_resource_manager_data_stream 10385 { 10386 ma_data_source_base ds; /* Base data source. A data stream is a data source. */ 10387 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ 10388 ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ 10389 ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ 10390 ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ 10391 ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ 10392 ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ 10393 MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ 10394 ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ 10395 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ 10396 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ 10397 10398 /* Written by the public API, read by the job thread. */ 10399 MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ 10400 10401 /* Written by the job thread, read by the public API. */ 10402 void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ 10403 MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ 10404 10405 /* Written and read by both the public API and the job thread. These must be atomic. */ 10406 MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ 10407 MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ 10408 MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ 10409 MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ 10410 }; 10411 10412 struct ma_resource_manager_data_source 10413 { 10414 union 10415 { 10416 ma_resource_manager_data_buffer buffer; 10417 ma_resource_manager_data_stream stream; 10418 } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ 10419 10420 ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ 10421 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ 10422 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ 10423 }; 10424 10425 typedef struct 10426 { 10427 ma_allocation_callbacks allocationCallbacks; 10428 ma_log* pLog; 10429 ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ 10430 ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ 10431 ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ 10432 ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ 10433 size_t jobThreadStackSize; 10434 ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ 10435 ma_uint32 flags; 10436 ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ 10437 ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; 10438 ma_uint32 customDecodingBackendCount; 10439 void* pCustomDecodingBackendUserData; 10440 } ma_resource_manager_config; 10441 10442 MA_API ma_resource_manager_config ma_resource_manager_config_init(void); 10443 10444 struct ma_resource_manager 10445 { 10446 ma_resource_manager_config config; 10447 ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ 10448 #ifndef MA_NO_THREADING 10449 ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ 10450 ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ 10451 #endif 10452 ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ 10453 ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ 10454 ma_log log; /* Only used if no log was specified in the config. */ 10455 }; 10456 10457 /* Init. */ 10458 MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); 10459 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); 10460 MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); 10461 10462 /* Registration. */ 10463 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); 10464 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); 10465 MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ 10466 MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); 10467 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ 10468 MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); 10469 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); 10470 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); 10471 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); 10472 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); 10473 10474 /* Data Buffers. */ 10475 MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); 10476 MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); 10477 MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); 10478 MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); 10479 MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); 10480 MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10481 MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); 10482 MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 10483 MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); 10484 MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); 10485 MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); 10486 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); 10487 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); 10488 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); 10489 10490 /* Data Streams. */ 10491 MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); 10492 MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); 10493 MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); 10494 MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); 10495 MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10496 MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); 10497 MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 10498 MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); 10499 MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); 10500 MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); 10501 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); 10502 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); 10503 MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); 10504 10505 /* Data Sources. */ 10506 MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); 10507 MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); 10508 MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); 10509 MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); 10510 MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); 10511 MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10512 MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); 10513 MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 10514 MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); 10515 MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); 10516 MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); 10517 MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); 10518 MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); 10519 MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); 10520 10521 /* Job management. */ 10522 MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); 10523 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ 10524 MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); 10525 MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ 10526 MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ 10527 #endif /* MA_NO_RESOURCE_MANAGER */ 10528 10529 10530 10531 /************************************************************************************************************************************************************ 10532 10533 Node Graph 10534 10535 ************************************************************************************************************************************************************/ 10536 #ifndef MA_NO_NODE_GRAPH 10537 /* Must never exceed 254. */ 10538 #ifndef MA_MAX_NODE_BUS_COUNT 10539 #define MA_MAX_NODE_BUS_COUNT 254 10540 #endif 10541 10542 /* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ 10543 #ifndef MA_MAX_NODE_LOCAL_BUS_COUNT 10544 #define MA_MAX_NODE_LOCAL_BUS_COUNT 2 10545 #endif 10546 10547 /* Use this when the bus count is determined by the node instance rather than the vtable. */ 10548 #define MA_NODE_BUS_COUNT_UNKNOWN 255 10549 10550 typedef struct ma_node_graph ma_node_graph; 10551 typedef void ma_node; 10552 10553 10554 /* Node flags. */ 10555 typedef enum 10556 { 10557 MA_NODE_FLAG_PASSTHROUGH = 0x00000001, 10558 MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, 10559 MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, 10560 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, 10561 MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 10562 } ma_node_flags; 10563 10564 10565 /* The playback state of a node. Either started or stopped. */ 10566 typedef enum 10567 { 10568 ma_node_state_started = 0, 10569 ma_node_state_stopped = 1 10570 } ma_node_state; 10571 10572 10573 typedef struct 10574 { 10575 /* 10576 Extended processing callback. This callback is used for effects that process input and output 10577 at different rates (i.e. they perform resampling). This is similar to the simple version, only 10578 they take two separate frame counts: one for input, and one for output. 10579 10580 On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas 10581 `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. 10582 10583 On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set 10584 `pFrameCountIn` to the number of input frames that were consumed. 10585 */ 10586 void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); 10587 10588 /* 10589 A callback for retrieving the number of a input frames that are required to output the 10590 specified number of output frames. You would only want to implement this when the node performs 10591 resampling. This is optional, even for nodes that perform resampling, but it does offer a 10592 small reduction in latency as it allows miniaudio to calculate the exact number of input frames 10593 to read at a time instead of having to estimate. 10594 */ 10595 ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); 10596 10597 /* 10598 The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` 10599 parameters of the callbacks above. 10600 */ 10601 ma_uint8 inputBusCount; 10602 10603 /* 10604 The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` 10605 parameters of the callbacks above. 10606 */ 10607 ma_uint8 outputBusCount; 10608 10609 /* 10610 Flags describing characteristics of the node. This is currently just a placeholder for some 10611 ideas for later on. 10612 */ 10613 ma_uint32 flags; 10614 } ma_node_vtable; 10615 10616 typedef struct 10617 { 10618 const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ 10619 ma_node_state initialState; /* Defaults to ma_node_state_started. */ 10620 ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ 10621 ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ 10622 const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ 10623 const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ 10624 } ma_node_config; 10625 10626 MA_API ma_node_config ma_node_config_init(void); 10627 10628 10629 /* 10630 A node has multiple output buses. An output bus is attached to an input bus as an item in a linked 10631 list. Think of the input bus as a linked list, with the output bus being an item in that list. 10632 */ 10633 typedef struct ma_node_output_bus ma_node_output_bus; 10634 struct ma_node_output_bus 10635 { 10636 /* Immutable. */ 10637 ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ 10638 ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ 10639 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ 10640 10641 /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ 10642 ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ 10643 MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ 10644 MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ 10645 MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ 10646 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ 10647 MA_ATOMIC(4, float) volume; /* Linear. */ 10648 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ 10649 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ 10650 MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ 10651 }; 10652 10653 /* 10654 A node has multiple input buses. The output buses of a node are connecting to the input busses of 10655 another. An input bus is essentially just a linked list of output buses. 10656 */ 10657 typedef struct ma_node_input_bus ma_node_input_bus; 10658 struct ma_node_input_bus 10659 { 10660 /* Mutable via multiple threads. */ 10661 ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ 10662 MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ 10663 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ 10664 10665 /* Set once at startup. */ 10666 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ 10667 }; 10668 10669 10670 typedef struct ma_node_base ma_node_base; 10671 struct ma_node_base 10672 { 10673 /* These variables are set once at startup. */ 10674 ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ 10675 const ma_node_vtable* vtable; 10676 float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ 10677 ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ 10678 10679 /* These variables are read and written only from the audio thread. */ 10680 ma_uint16 cachedFrameCountOut; 10681 ma_uint16 cachedFrameCountIn; 10682 ma_uint16 consumedFrameCountIn; 10683 10684 /* These variables are read and written between different threads. */ 10685 MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ 10686 MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ 10687 MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ 10688 ma_uint32 inputBusCount; 10689 ma_uint32 outputBusCount; 10690 ma_node_input_bus* pInputBuses; 10691 ma_node_output_bus* pOutputBuses; 10692 10693 /* Memory management. */ 10694 ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; 10695 ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; 10696 void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ 10697 ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ 10698 }; 10699 10700 MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); 10701 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); 10702 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); 10703 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10704 MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); 10705 MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); 10706 MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); 10707 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); 10708 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); 10709 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); 10710 MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); 10711 MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); 10712 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); 10713 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); 10714 MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); 10715 MA_API ma_node_state ma_node_get_state(const ma_node* pNode); 10716 MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); 10717 MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); 10718 MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); 10719 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); 10720 MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); 10721 MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); 10722 10723 10724 typedef struct 10725 { 10726 ma_uint32 channels; 10727 ma_uint16 nodeCacheCapInFrames; 10728 } ma_node_graph_config; 10729 10730 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); 10731 10732 10733 struct ma_node_graph 10734 { 10735 /* Immutable. */ 10736 ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ 10737 ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ 10738 ma_uint16 nodeCacheCapInFrames; 10739 10740 /* Read and written by multiple threads. */ 10741 MA_ATOMIC(4, ma_bool32) isReading; 10742 }; 10743 10744 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); 10745 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); 10746 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); 10747 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 10748 MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); 10749 MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); 10750 MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); 10751 10752 10753 10754 /* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ 10755 typedef struct 10756 { 10757 ma_node_config nodeConfig; 10758 ma_data_source* pDataSource; 10759 } ma_data_source_node_config; 10760 10761 MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); 10762 10763 10764 typedef struct 10765 { 10766 ma_node_base base; 10767 ma_data_source* pDataSource; 10768 } ma_data_source_node; 10769 10770 MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); 10771 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); 10772 MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); 10773 MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); 10774 10775 10776 /* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ 10777 typedef struct 10778 { 10779 ma_node_config nodeConfig; 10780 ma_uint32 channels; 10781 ma_uint32 outputBusCount; 10782 } ma_splitter_node_config; 10783 10784 MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); 10785 10786 10787 typedef struct 10788 { 10789 ma_node_base base; 10790 } ma_splitter_node; 10791 10792 MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); 10793 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); 10794 10795 10796 /* 10797 Biquad Node 10798 */ 10799 typedef struct 10800 { 10801 ma_node_config nodeConfig; 10802 ma_biquad_config biquad; 10803 } ma_biquad_node_config; 10804 10805 MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); 10806 10807 10808 typedef struct 10809 { 10810 ma_node_base baseNode; 10811 ma_biquad biquad; 10812 } ma_biquad_node; 10813 10814 MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); 10815 MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); 10816 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10817 10818 10819 /* 10820 Low Pass Filter Node 10821 */ 10822 typedef struct 10823 { 10824 ma_node_config nodeConfig; 10825 ma_lpf_config lpf; 10826 } ma_lpf_node_config; 10827 10828 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 10829 10830 10831 typedef struct 10832 { 10833 ma_node_base baseNode; 10834 ma_lpf lpf; 10835 } ma_lpf_node; 10836 10837 MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); 10838 MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); 10839 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10840 10841 10842 /* 10843 High Pass Filter Node 10844 */ 10845 typedef struct 10846 { 10847 ma_node_config nodeConfig; 10848 ma_hpf_config hpf; 10849 } ma_hpf_node_config; 10850 10851 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 10852 10853 10854 typedef struct 10855 { 10856 ma_node_base baseNode; 10857 ma_hpf hpf; 10858 } ma_hpf_node; 10859 10860 MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); 10861 MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); 10862 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10863 10864 10865 /* 10866 Band Pass Filter Node 10867 */ 10868 typedef struct 10869 { 10870 ma_node_config nodeConfig; 10871 ma_bpf_config bpf; 10872 } ma_bpf_node_config; 10873 10874 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); 10875 10876 10877 typedef struct 10878 { 10879 ma_node_base baseNode; 10880 ma_bpf bpf; 10881 } ma_bpf_node; 10882 10883 MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); 10884 MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); 10885 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10886 10887 10888 /* 10889 Notching Filter Node 10890 */ 10891 typedef struct 10892 { 10893 ma_node_config nodeConfig; 10894 ma_notch_config notch; 10895 } ma_notch_node_config; 10896 10897 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); 10898 10899 10900 typedef struct 10901 { 10902 ma_node_base baseNode; 10903 ma_notch2 notch; 10904 } ma_notch_node; 10905 10906 MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); 10907 MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); 10908 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10909 10910 10911 /* 10912 Peaking Filter Node 10913 */ 10914 typedef struct 10915 { 10916 ma_node_config nodeConfig; 10917 ma_peak_config peak; 10918 } ma_peak_node_config; 10919 10920 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); 10921 10922 10923 typedef struct 10924 { 10925 ma_node_base baseNode; 10926 ma_peak2 peak; 10927 } ma_peak_node; 10928 10929 MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); 10930 MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); 10931 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10932 10933 10934 /* 10935 Low Shelf Filter Node 10936 */ 10937 typedef struct 10938 { 10939 ma_node_config nodeConfig; 10940 ma_loshelf_config loshelf; 10941 } ma_loshelf_node_config; 10942 10943 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); 10944 10945 10946 typedef struct 10947 { 10948 ma_node_base baseNode; 10949 ma_loshelf2 loshelf; 10950 } ma_loshelf_node; 10951 10952 MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); 10953 MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); 10954 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10955 10956 10957 /* 10958 High Shelf Filter Node 10959 */ 10960 typedef struct 10961 { 10962 ma_node_config nodeConfig; 10963 ma_hishelf_config hishelf; 10964 } ma_hishelf_node_config; 10965 10966 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); 10967 10968 10969 typedef struct 10970 { 10971 ma_node_base baseNode; 10972 ma_hishelf2 hishelf; 10973 } ma_hishelf_node; 10974 10975 MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); 10976 MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); 10977 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); 10978 10979 10980 typedef struct 10981 { 10982 ma_node_config nodeConfig; 10983 ma_delay_config delay; 10984 } ma_delay_node_config; 10985 10986 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); 10987 10988 10989 typedef struct 10990 { 10991 ma_node_base baseNode; 10992 ma_delay delay; 10993 } ma_delay_node; 10994 10995 MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); 10996 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); 10997 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); 10998 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); 10999 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); 11000 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); 11001 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); 11002 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); 11003 #endif /* MA_NO_NODE_GRAPH */ 11004 11005 11006 /* SECTION: miniaudio_engine.h */ 11007 /************************************************************************************************************************************************************ 11008 11009 Engine 11010 11011 ************************************************************************************************************************************************************/ 11012 #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) 11013 typedef struct ma_engine ma_engine; 11014 typedef struct ma_sound ma_sound; 11015 11016 11017 /* Sound flags. */ 11018 typedef enum 11019 { 11020 /* Resource manager flags. */ 11021 MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ 11022 MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ 11023 MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ 11024 MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ 11025 MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ 11026 11027 /* ma_sound specific flags. */ 11028 MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ 11029 MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ 11030 MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ 11031 } ma_sound_flags; 11032 11033 #ifndef MA_ENGINE_MAX_LISTENERS 11034 #define MA_ENGINE_MAX_LISTENERS 4 11035 #endif 11036 11037 #define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) 11038 11039 typedef enum 11040 { 11041 ma_engine_node_type_sound, 11042 ma_engine_node_type_group 11043 } ma_engine_node_type; 11044 11045 typedef struct 11046 { 11047 ma_engine* pEngine; 11048 ma_engine_node_type type; 11049 ma_uint32 channelsIn; 11050 ma_uint32 channelsOut; 11051 ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ 11052 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ 11053 ma_mono_expansion_mode monoExpansionMode; 11054 ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ 11055 ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ 11056 ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ 11057 } ma_engine_node_config; 11058 11059 MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); 11060 11061 11062 /* Base node object for both ma_sound and ma_sound_group. */ 11063 typedef struct 11064 { 11065 ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ 11066 ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ 11067 ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ 11068 ma_uint32 volumeSmoothTimeInPCMFrames; 11069 ma_mono_expansion_mode monoExpansionMode; 11070 ma_fader fader; 11071 ma_linear_resampler resampler; /* For pitch shift. */ 11072 ma_spatializer spatializer; 11073 ma_panner panner; 11074 ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ 11075 ma_atomic_float volume; /* Defaults to 1. */ 11076 MA_ATOMIC(4, float) pitch; 11077 float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ 11078 float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ 11079 MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ 11080 MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ 11081 MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ 11082 11083 /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ 11084 struct 11085 { 11086 ma_atomic_float volumeBeg; 11087 ma_atomic_float volumeEnd; 11088 ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ 11089 ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ 11090 } fadeSettings; 11091 11092 /* Memory management. */ 11093 ma_bool8 _ownsHeap; 11094 void* _pHeap; 11095 } ma_engine_node; 11096 11097 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); 11098 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); 11099 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); 11100 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); 11101 11102 11103 #define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF 11104 11105 /* Callback for when a sound reaches the end. */ 11106 typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); 11107 11108 typedef struct 11109 { 11110 const char* pFilePath; /* Set this to load from the resource manager. */ 11111 const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ 11112 ma_data_source* pDataSource; /* Set this to load from an existing data source. */ 11113 ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ 11114 ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ 11115 ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ 11116 ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ 11117 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ 11118 ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ 11119 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ 11120 ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ 11121 ma_uint64 rangeBegInPCMFrames; 11122 ma_uint64 rangeEndInPCMFrames; 11123 ma_uint64 loopPointBegInPCMFrames; 11124 ma_uint64 loopPointEndInPCMFrames; 11125 ma_bool32 isLooping; 11126 ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ 11127 void* pEndCallbackUserData; 11128 #ifndef MA_NO_RESOURCE_MANAGER 11129 ma_resource_manager_pipeline_notifications initNotifications; 11130 #endif 11131 ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ 11132 } ma_sound_config; 11133 11134 MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ 11135 MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ 11136 11137 struct ma_sound 11138 { 11139 ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ 11140 ma_data_source* pDataSource; 11141 MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ 11142 MA_ATOMIC(4, ma_bool32) atEnd; 11143 ma_sound_end_proc endCallback; 11144 void* pEndCallbackUserData; 11145 ma_bool8 ownsDataSource; 11146 11147 /* 11148 We're declaring a resource manager data source object here to save us a malloc when loading a 11149 sound via the resource manager, which I *think* will be the most common scenario. 11150 */ 11151 #ifndef MA_NO_RESOURCE_MANAGER 11152 ma_resource_manager_data_source* pResourceManagerDataSource; 11153 #endif 11154 }; 11155 11156 /* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ 11157 typedef struct ma_sound_inlined ma_sound_inlined; 11158 struct ma_sound_inlined 11159 { 11160 ma_sound sound; 11161 ma_sound_inlined* pNext; 11162 ma_sound_inlined* pPrev; 11163 }; 11164 11165 /* A sound group is just a sound. */ 11166 typedef ma_sound_config ma_sound_group_config; 11167 typedef ma_sound ma_sound_group; 11168 11169 MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ 11170 MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ 11171 11172 typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); 11173 11174 typedef struct 11175 { 11176 #if !defined(MA_NO_RESOURCE_MANAGER) 11177 ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ 11178 #endif 11179 #if !defined(MA_NO_DEVICE_IO) 11180 ma_context* pContext; 11181 ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ 11182 ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ 11183 ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ 11184 ma_device_notification_proc notificationCallback; 11185 #endif 11186 ma_log* pLog; /* When set to NULL, will use the context's log. */ 11187 ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ 11188 ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ 11189 ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ 11190 ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ 11191 ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ 11192 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ 11193 ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ 11194 ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ 11195 ma_allocation_callbacks allocationCallbacks; 11196 ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ 11197 ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ 11198 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ 11199 ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ 11200 ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ 11201 void* pProcessUserData; /* User data that's passed into onProcess. */ 11202 } ma_engine_config; 11203 11204 MA_API ma_engine_config ma_engine_config_init(void); 11205 11206 11207 struct ma_engine 11208 { 11209 ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ 11210 #if !defined(MA_NO_RESOURCE_MANAGER) 11211 ma_resource_manager* pResourceManager; 11212 #endif 11213 #if !defined(MA_NO_DEVICE_IO) 11214 ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ 11215 #endif 11216 ma_log* pLog; 11217 ma_uint32 sampleRate; 11218 ma_uint32 listenerCount; 11219 ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; 11220 ma_allocation_callbacks allocationCallbacks; 11221 ma_bool8 ownsResourceManager; 11222 ma_bool8 ownsDevice; 11223 ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ 11224 ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ 11225 MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ 11226 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ 11227 ma_uint32 defaultVolumeSmoothTimeInPCMFrames; 11228 ma_mono_expansion_mode monoExpansionMode; 11229 ma_engine_process_proc onProcess; 11230 void* pProcessUserData; 11231 }; 11232 11233 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); 11234 MA_API void ma_engine_uninit(ma_engine* pEngine); 11235 MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 11236 MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); 11237 #if !defined(MA_NO_RESOURCE_MANAGER) 11238 MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); 11239 #endif 11240 MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); 11241 MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); 11242 MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); 11243 MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); 11244 MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); 11245 MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); 11246 MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); 11247 MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ 11248 MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ 11249 MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); 11250 MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); 11251 11252 MA_API ma_result ma_engine_start(ma_engine* pEngine); 11253 MA_API ma_result ma_engine_stop(ma_engine* pEngine); 11254 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); 11255 MA_API float ma_engine_get_volume(ma_engine* pEngine); 11256 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); 11257 MA_API float ma_engine_get_gain_db(ma_engine* pEngine); 11258 11259 MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); 11260 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); 11261 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); 11262 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); 11263 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); 11264 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); 11265 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); 11266 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); 11267 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); 11268 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); 11269 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); 11270 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); 11271 MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); 11272 MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); 11273 11274 #ifndef MA_NO_RESOURCE_MANAGER 11275 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); 11276 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ 11277 #endif 11278 11279 #ifndef MA_NO_RESOURCE_MANAGER 11280 MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); 11281 MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); 11282 MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); 11283 #endif 11284 MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); 11285 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); 11286 MA_API void ma_sound_uninit(ma_sound* pSound); 11287 MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); 11288 MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); 11289 MA_API ma_result ma_sound_start(ma_sound* pSound); 11290 MA_API ma_result ma_sound_stop(ma_sound* pSound); 11291 MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ 11292 MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ 11293 MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); 11294 MA_API float ma_sound_get_volume(const ma_sound* pSound); 11295 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); 11296 MA_API float ma_sound_get_pan(const ma_sound* pSound); 11297 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); 11298 MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); 11299 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); 11300 MA_API float ma_sound_get_pitch(const ma_sound* pSound); 11301 MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); 11302 MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); 11303 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); 11304 MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); 11305 MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); 11306 MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); 11307 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); 11308 MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); 11309 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); 11310 MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); 11311 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); 11312 MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); 11313 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); 11314 MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); 11315 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); 11316 MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); 11317 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); 11318 MA_API float ma_sound_get_rolloff(const ma_sound* pSound); 11319 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); 11320 MA_API float ma_sound_get_min_gain(const ma_sound* pSound); 11321 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); 11322 MA_API float ma_sound_get_max_gain(const ma_sound* pSound); 11323 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); 11324 MA_API float ma_sound_get_min_distance(const ma_sound* pSound); 11325 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); 11326 MA_API float ma_sound_get_max_distance(const ma_sound* pSound); 11327 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); 11328 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); 11329 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); 11330 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); 11331 MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); 11332 MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); 11333 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); 11334 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); 11335 MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); 11336 MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); 11337 MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); 11338 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); 11339 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); 11340 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); 11341 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); 11342 MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); 11343 MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); 11344 MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); 11345 MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); 11346 MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); 11347 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); 11348 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); 11349 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); 11350 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ 11351 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 11352 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); 11353 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); 11354 MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); 11355 MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); 11356 MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); 11357 11358 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); 11359 MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); 11360 MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); 11361 MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); 11362 MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); 11363 MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); 11364 MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); 11365 MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); 11366 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); 11367 MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); 11368 MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); 11369 MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); 11370 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); 11371 MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); 11372 MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); 11373 MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); 11374 MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); 11375 MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); 11376 MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); 11377 MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); 11378 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); 11379 MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); 11380 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); 11381 MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); 11382 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); 11383 MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); 11384 MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); 11385 MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); 11386 MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); 11387 MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); 11388 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); 11389 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); 11390 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); 11391 MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); 11392 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); 11393 MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); 11394 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); 11395 MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); 11396 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); 11397 MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); 11398 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); 11399 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); 11400 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); 11401 MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); 11402 MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); 11403 MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); 11404 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); 11405 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); 11406 MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); 11407 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); 11408 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); 11409 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); 11410 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); 11411 MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); 11412 MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); 11413 #endif /* MA_NO_ENGINE */ 11414 /* END SECTION: miniaudio_engine.h */ 11415 11416 #ifdef __cplusplus 11417 } 11418 #endif 11419 #endif /* miniaudio_h */ 11420 11421 11422 /* 11423 This is for preventing greying out of the implementation section. 11424 */ 11425 #if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) 11426 #define MINIAUDIO_IMPLEMENTATION 11427 #endif 11428 11429 /************************************************************************************************************************************************************ 11430 ************************************************************************************************************************************************************* 11431 11432 IMPLEMENTATION 11433 11434 ************************************************************************************************************************************************************* 11435 ************************************************************************************************************************************************************/ 11436 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) 11437 #ifndef miniaudio_c 11438 #define miniaudio_c 11439 11440 #include <assert.h> 11441 #include <limits.h> /* For INT_MAX */ 11442 #include <math.h> /* sin(), etc. */ 11443 #include <stdlib.h> /* For malloc(), free(), wcstombs(). */ 11444 #include <string.h> /* For memset() */ 11445 11446 #include <stdarg.h> 11447 #include <stdio.h> 11448 #if !defined(_MSC_VER) && !defined(__DMC__) 11449 #include <strings.h> /* For strcasecmp(). */ 11450 #include <wchar.h> /* For wcslen(), wcsrtombs() */ 11451 #endif 11452 #ifdef _MSC_VER 11453 #include <float.h> /* For _controlfp_s constants */ 11454 #endif 11455 11456 #if defined(MA_WIN32) 11457 #include <windows.h> 11458 11459 /* 11460 There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols 11461 such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're 11462 unavailable. 11463 */ 11464 #ifndef STGM_READ 11465 #define STGM_READ 0x00000000L 11466 #endif 11467 #ifndef CLSCTX_ALL 11468 #define CLSCTX_ALL 23 11469 #endif 11470 11471 /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ 11472 typedef struct ma_IUnknown ma_IUnknown; 11473 #endif 11474 11475 #if !defined(MA_WIN32) 11476 #include <sched.h> 11477 #include <sys/time.h> /* select() (used for ma_sleep()). */ 11478 #include <pthread.h> 11479 #endif 11480 11481 #ifdef MA_NX 11482 #include <time.h> /* For nanosleep() */ 11483 #endif 11484 11485 #include <sys/stat.h> /* For fstat(), etc. */ 11486 11487 #ifdef MA_EMSCRIPTEN 11488 #include <emscripten/emscripten.h> 11489 #endif 11490 11491 11492 /* Architecture Detection */ 11493 #if !defined(MA_64BIT) && !defined(MA_32BIT) 11494 #ifdef _WIN32 11495 #ifdef _WIN64 11496 #define MA_64BIT 11497 #else 11498 #define MA_32BIT 11499 #endif 11500 #endif 11501 #endif 11502 11503 #if !defined(MA_64BIT) && !defined(MA_32BIT) 11504 #ifdef __GNUC__ 11505 #ifdef __LP64__ 11506 #define MA_64BIT 11507 #else 11508 #define MA_32BIT 11509 #endif 11510 #endif 11511 #endif 11512 11513 #if !defined(MA_64BIT) && !defined(MA_32BIT) 11514 #include <stdint.h> 11515 #if INTPTR_MAX == INT64_MAX 11516 #define MA_64BIT 11517 #else 11518 #define MA_32BIT 11519 #endif 11520 #endif 11521 11522 #if defined(__arm__) || defined(_M_ARM) 11523 #define MA_ARM32 11524 #endif 11525 #if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) 11526 #define MA_ARM64 11527 #endif 11528 11529 #if defined(__x86_64__) || defined(_M_X64) 11530 #define MA_X64 11531 #elif defined(__i386) || defined(_M_IX86) 11532 #define MA_X86 11533 #elif defined(MA_ARM32) || defined(MA_ARM64) 11534 #define MA_ARM 11535 #endif 11536 11537 /* Intrinsics Support */ 11538 #if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) 11539 #if defined(_MSC_VER) && !defined(__clang__) 11540 /* MSVC. */ 11541 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ 11542 #define MA_SUPPORT_SSE2 11543 #endif 11544 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ 11545 /* #define MA_SUPPORT_AVX*/ 11546 /*#endif*/ 11547 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ 11548 #define MA_SUPPORT_AVX2 11549 #endif 11550 #else 11551 /* Assume GNUC-style. */ 11552 #if defined(__SSE2__) && !defined(MA_NO_SSE2) 11553 #define MA_SUPPORT_SSE2 11554 #endif 11555 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ 11556 /* #define MA_SUPPORT_AVX*/ 11557 /*#endif*/ 11558 #if defined(__AVX2__) && !defined(MA_NO_AVX2) 11559 #define MA_SUPPORT_AVX2 11560 #endif 11561 #endif 11562 11563 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ 11564 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) 11565 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>) 11566 #define MA_SUPPORT_SSE2 11567 #endif 11568 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/ 11569 /* #define MA_SUPPORT_AVX*/ 11570 /*#endif*/ 11571 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>) 11572 #define MA_SUPPORT_AVX2 11573 #endif 11574 #endif 11575 11576 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) 11577 #include <immintrin.h> 11578 #elif defined(MA_SUPPORT_SSE2) 11579 #include <emmintrin.h> 11580 #endif 11581 #endif 11582 11583 #if defined(MA_ARM) 11584 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) 11585 #define MA_SUPPORT_NEON 11586 #include <arm_neon.h> 11587 #endif 11588 #endif 11589 11590 /* Begin globally disabled warnings. */ 11591 #if defined(_MSC_VER) 11592 #pragma warning(push) 11593 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ 11594 #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ 11595 #endif 11596 11597 #if defined(MA_X64) || defined(MA_X86) 11598 #if defined(_MSC_VER) && !defined(__clang__) 11599 #if _MSC_VER >= 1400 11600 #include <intrin.h> 11601 static MA_INLINE void ma_cpuid(int info[4], int fid) 11602 { 11603 __cpuid(info, fid); 11604 } 11605 #else 11606 #define MA_NO_CPUID 11607 #endif 11608 11609 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) 11610 static MA_INLINE unsigned __int64 ma_xgetbv(int reg) 11611 { 11612 return _xgetbv(reg); 11613 } 11614 #else 11615 #define MA_NO_XGETBV 11616 #endif 11617 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) 11618 static MA_INLINE void ma_cpuid(int info[4], int fid) 11619 { 11620 /* 11621 It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the 11622 specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for 11623 supporting different assembly dialects. 11624 11625 What's basically happening is that we're saving and restoring the ebx register manually. 11626 */ 11627 #if defined(MA_X86) && defined(__PIC__) 11628 __asm__ __volatile__ ( 11629 "xchg{l} {%%}ebx, %k1;" 11630 "cpuid;" 11631 "xchg{l} {%%}ebx, %k1;" 11632 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) 11633 ); 11634 #else 11635 __asm__ __volatile__ ( 11636 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) 11637 ); 11638 #endif 11639 } 11640 11641 static MA_INLINE ma_uint64 ma_xgetbv(int reg) 11642 { 11643 unsigned int hi; 11644 unsigned int lo; 11645 11646 __asm__ __volatile__ ( 11647 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) 11648 ); 11649 11650 return ((ma_uint64)hi << 32) | (ma_uint64)lo; 11651 } 11652 #else 11653 #define MA_NO_CPUID 11654 #define MA_NO_XGETBV 11655 #endif 11656 #else 11657 #define MA_NO_CPUID 11658 #define MA_NO_XGETBV 11659 #endif 11660 11661 static MA_INLINE ma_bool32 ma_has_sse2(void) 11662 { 11663 #if defined(MA_SUPPORT_SSE2) 11664 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) 11665 #if defined(MA_X64) 11666 return MA_TRUE; /* 64-bit targets always support SSE2. */ 11667 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) 11668 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ 11669 #else 11670 #if defined(MA_NO_CPUID) 11671 return MA_FALSE; 11672 #else 11673 int info[4]; 11674 ma_cpuid(info, 1); 11675 return (info[3] & (1 << 26)) != 0; 11676 #endif 11677 #endif 11678 #else 11679 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ 11680 #endif 11681 #else 11682 return MA_FALSE; /* No compiler support. */ 11683 #endif 11684 } 11685 11686 #if 0 11687 static MA_INLINE ma_bool32 ma_has_avx() 11688 { 11689 #if defined(MA_SUPPORT_AVX) 11690 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) 11691 #if defined(_AVX_) || defined(__AVX__) 11692 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ 11693 #else 11694 /* AVX requires both CPU and OS support. */ 11695 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) 11696 return MA_FALSE; 11697 #else 11698 int info[4]; 11699 ma_cpuid(info, 1); 11700 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { 11701 ma_uint64 xrc = ma_xgetbv(0); 11702 if ((xrc & 0x06) == 0x06) { 11703 return MA_TRUE; 11704 } else { 11705 return MA_FALSE; 11706 } 11707 } else { 11708 return MA_FALSE; 11709 } 11710 #endif 11711 #endif 11712 #else 11713 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ 11714 #endif 11715 #else 11716 return MA_FALSE; /* No compiler support. */ 11717 #endif 11718 } 11719 #endif 11720 11721 static MA_INLINE ma_bool32 ma_has_avx2(void) 11722 { 11723 #if defined(MA_SUPPORT_AVX2) 11724 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) 11725 #if defined(_AVX2_) || defined(__AVX2__) 11726 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ 11727 #else 11728 /* AVX2 requires both CPU and OS support. */ 11729 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) 11730 return MA_FALSE; 11731 #else 11732 int info1[4]; 11733 int info7[4]; 11734 ma_cpuid(info1, 1); 11735 ma_cpuid(info7, 7); 11736 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { 11737 ma_uint64 xrc = ma_xgetbv(0); 11738 if ((xrc & 0x06) == 0x06) { 11739 return MA_TRUE; 11740 } else { 11741 return MA_FALSE; 11742 } 11743 } else { 11744 return MA_FALSE; 11745 } 11746 #endif 11747 #endif 11748 #else 11749 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ 11750 #endif 11751 #else 11752 return MA_FALSE; /* No compiler support. */ 11753 #endif 11754 } 11755 11756 static MA_INLINE ma_bool32 ma_has_neon(void) 11757 { 11758 #if defined(MA_SUPPORT_NEON) 11759 #if defined(MA_ARM) && !defined(MA_NO_NEON) 11760 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) 11761 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ 11762 #else 11763 /* TODO: Runtime check. */ 11764 return MA_FALSE; 11765 #endif 11766 #else 11767 return MA_FALSE; /* NEON is only supported on ARM architectures. */ 11768 #endif 11769 #else 11770 return MA_FALSE; /* No compiler support. */ 11771 #endif 11772 } 11773 11774 #if defined(__has_builtin) 11775 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) 11776 #else 11777 #define MA_COMPILER_HAS_BUILTIN(x) 0 11778 #endif 11779 11780 #ifndef MA_ASSUME 11781 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) 11782 #define MA_ASSUME(x) __builtin_assume(x) 11783 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) 11784 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) 11785 #elif defined(_MSC_VER) 11786 #define MA_ASSUME(x) __assume(x) 11787 #else 11788 #define MA_ASSUME(x) (void)(x) 11789 #endif 11790 #endif 11791 11792 #ifndef MA_RESTRICT 11793 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) 11794 #define MA_RESTRICT __restrict 11795 #else 11796 #define MA_RESTRICT 11797 #endif 11798 #endif 11799 11800 #if defined(_MSC_VER) && _MSC_VER >= 1400 11801 #define MA_HAS_BYTESWAP16_INTRINSIC 11802 #define MA_HAS_BYTESWAP32_INTRINSIC 11803 #define MA_HAS_BYTESWAP64_INTRINSIC 11804 #elif defined(__clang__) 11805 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) 11806 #define MA_HAS_BYTESWAP16_INTRINSIC 11807 #endif 11808 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) 11809 #define MA_HAS_BYTESWAP32_INTRINSIC 11810 #endif 11811 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) 11812 #define MA_HAS_BYTESWAP64_INTRINSIC 11813 #endif 11814 #elif defined(__GNUC__) 11815 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) 11816 #define MA_HAS_BYTESWAP32_INTRINSIC 11817 #define MA_HAS_BYTESWAP64_INTRINSIC 11818 #endif 11819 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 11820 #define MA_HAS_BYTESWAP16_INTRINSIC 11821 #endif 11822 #endif 11823 11824 11825 static MA_INLINE ma_bool32 ma_is_little_endian(void) 11826 { 11827 #if defined(MA_X86) || defined(MA_X64) 11828 return MA_TRUE; 11829 #else 11830 int n = 1; 11831 return (*(char*)&n) == 1; 11832 #endif 11833 } 11834 11835 static MA_INLINE ma_bool32 ma_is_big_endian(void) 11836 { 11837 return !ma_is_little_endian(); 11838 } 11839 11840 11841 static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) 11842 { 11843 #ifdef MA_HAS_BYTESWAP32_INTRINSIC 11844 #if defined(_MSC_VER) 11845 return _byteswap_ulong(n); 11846 #elif defined(__GNUC__) || defined(__clang__) 11847 #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ 11848 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ 11849 ma_uint32 r; 11850 __asm__ __volatile__ ( 11851 #if defined(MA_64BIT) 11852 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ 11853 #else 11854 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) 11855 #endif 11856 ); 11857 return r; 11858 #else 11859 return __builtin_bswap32(n); 11860 #endif 11861 #else 11862 #error "This compiler does not support the byte swap intrinsic." 11863 #endif 11864 #else 11865 return ((n & 0xFF000000) >> 24) | 11866 ((n & 0x00FF0000) >> 8) | 11867 ((n & 0x0000FF00) << 8) | 11868 ((n & 0x000000FF) << 24); 11869 #endif 11870 } 11871 11872 11873 #if !defined(MA_EMSCRIPTEN) 11874 #ifdef MA_WIN32 11875 static void ma_sleep__win32(ma_uint32 milliseconds) 11876 { 11877 Sleep((DWORD)milliseconds); 11878 } 11879 #endif 11880 #ifdef MA_POSIX 11881 static void ma_sleep__posix(ma_uint32 milliseconds) 11882 { 11883 #ifdef MA_EMSCRIPTEN 11884 (void)milliseconds; 11885 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ 11886 #else 11887 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) 11888 struct timespec ts; 11889 ts.tv_sec = milliseconds / 1000; 11890 ts.tv_nsec = milliseconds % 1000 * 1000000; 11891 nanosleep(&ts, NULL); 11892 #else 11893 struct timeval tv; 11894 tv.tv_sec = milliseconds / 1000; 11895 tv.tv_usec = milliseconds % 1000 * 1000; 11896 select(0, NULL, NULL, NULL, &tv); 11897 #endif 11898 #endif 11899 } 11900 #endif 11901 11902 static MA_INLINE void ma_sleep(ma_uint32 milliseconds) 11903 { 11904 #ifdef MA_WIN32 11905 ma_sleep__win32(milliseconds); 11906 #endif 11907 #ifdef MA_POSIX 11908 ma_sleep__posix(milliseconds); 11909 #endif 11910 } 11911 #endif 11912 11913 static MA_INLINE void ma_yield(void) 11914 { 11915 #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) 11916 /* x86/x64 */ 11917 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) 11918 #if _MSC_VER >= 1400 11919 _mm_pause(); 11920 #else 11921 #if defined(__DMC__) 11922 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ 11923 __asm nop; 11924 #else 11925 __asm pause; 11926 #endif 11927 #endif 11928 #else 11929 __asm__ __volatile__ ("pause"); 11930 #endif 11931 #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) 11932 /* ARM */ 11933 #if defined(_MSC_VER) 11934 /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ 11935 __yield(); 11936 #else 11937 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ 11938 #endif 11939 #else 11940 /* Unknown or unsupported architecture. No-op. */ 11941 #endif 11942 } 11943 11944 11945 #define MA_MM_DENORMALS_ZERO_MASK 0x0040 11946 #define MA_MM_FLUSH_ZERO_MASK 0x8000 11947 11948 static MA_INLINE unsigned int ma_disable_denormals(void) 11949 { 11950 unsigned int prevState; 11951 11952 #if defined(_MSC_VER) 11953 { 11954 /* 11955 Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't 11956 know which version of Visual Studio first added support for _controlfp_s(), but I do know 11957 that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older 11958 versions of Visual Studio, let me know and I'll make the necessary adjustment. 11959 */ 11960 #if _MSC_VER <= 1200 11961 { 11962 prevState = _statusfp(); 11963 _controlfp(prevState | _DN_FLUSH, _MCW_DN); 11964 } 11965 #else 11966 { 11967 unsigned int unused; 11968 _controlfp_s(&prevState, 0, 0); 11969 _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); 11970 } 11971 #endif 11972 } 11973 #elif defined(MA_X86) || defined(MA_X64) 11974 { 11975 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ 11976 { 11977 prevState = _mm_getcsr(); 11978 _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); 11979 } 11980 #else 11981 { 11982 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ 11983 prevState = 0; 11984 } 11985 #endif 11986 } 11987 #else 11988 { 11989 /* Unknown or unsupported architecture. No-op. */ 11990 prevState = 0; 11991 } 11992 #endif 11993 11994 return prevState; 11995 } 11996 11997 static MA_INLINE void ma_restore_denormals(unsigned int prevState) 11998 { 11999 #if defined(_MSC_VER) 12000 { 12001 /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ 12002 #if _MSC_VER <= 1200 12003 { 12004 _controlfp(prevState, _MCW_DN); 12005 } 12006 #else 12007 { 12008 unsigned int unused; 12009 _controlfp_s(&unused, prevState, _MCW_DN); 12010 } 12011 #endif 12012 } 12013 #elif defined(MA_X86) || defined(MA_X64) 12014 { 12015 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ 12016 { 12017 _mm_setcsr(prevState); 12018 } 12019 #else 12020 { 12021 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ 12022 (void)prevState; 12023 } 12024 #endif 12025 } 12026 #else 12027 { 12028 /* Unknown or unsupported architecture. No-op. */ 12029 (void)prevState; 12030 } 12031 #endif 12032 } 12033 12034 12035 #ifdef MA_ANDROID 12036 #include <sys/system_properties.h> 12037 12038 int ma_android_sdk_version() 12039 { 12040 char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; 12041 if (__system_property_get("ro.build.version.sdk", sdkVersion)) { 12042 return atoi(sdkVersion); 12043 } 12044 12045 return 0; 12046 } 12047 #endif 12048 12049 12050 #ifndef MA_COINIT_VALUE 12051 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ 12052 #endif 12053 12054 12055 #ifndef MA_FLT_MAX 12056 #ifdef FLT_MAX 12057 #define MA_FLT_MAX FLT_MAX 12058 #else 12059 #define MA_FLT_MAX 3.402823466e+38F 12060 #endif 12061 #endif 12062 12063 12064 #ifndef MA_PI 12065 #define MA_PI 3.14159265358979323846264f 12066 #endif 12067 #ifndef MA_PI_D 12068 #define MA_PI_D 3.14159265358979323846264 12069 #endif 12070 #ifndef MA_TAU 12071 #define MA_TAU 6.28318530717958647693f 12072 #endif 12073 #ifndef MA_TAU_D 12074 #define MA_TAU_D 6.28318530717958647693 12075 #endif 12076 12077 12078 /* The default format when ma_format_unknown (0) is requested when initializing a device. */ 12079 #ifndef MA_DEFAULT_FORMAT 12080 #define MA_DEFAULT_FORMAT ma_format_f32 12081 #endif 12082 12083 /* The default channel count to use when 0 is used when initializing a device. */ 12084 #ifndef MA_DEFAULT_CHANNELS 12085 #define MA_DEFAULT_CHANNELS 2 12086 #endif 12087 12088 /* The default sample rate to use when 0 is used when initializing a device. */ 12089 #ifndef MA_DEFAULT_SAMPLE_RATE 12090 #define MA_DEFAULT_SAMPLE_RATE 48000 12091 #endif 12092 12093 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ 12094 #ifndef MA_DEFAULT_PERIODS 12095 #define MA_DEFAULT_PERIODS 3 12096 #endif 12097 12098 /* The default period size in milliseconds for low latency mode. */ 12099 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 12100 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 12101 #endif 12102 12103 /* The default buffer size in milliseconds for conservative mode. */ 12104 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 12105 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 12106 #endif 12107 12108 /* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ 12109 #ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER 12110 #if MA_MAX_FILTER_ORDER >= 4 12111 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 12112 #else 12113 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER 12114 #endif 12115 #endif 12116 12117 12118 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 12119 #pragma GCC diagnostic push 12120 #pragma GCC diagnostic ignored "-Wunused-variable" 12121 #endif 12122 12123 /* Standard sample rates, in order of priority. */ 12124 static ma_uint32 g_maStandardSampleRatePriorities[] = { 12125 (ma_uint32)ma_standard_sample_rate_48000, 12126 (ma_uint32)ma_standard_sample_rate_44100, 12127 12128 (ma_uint32)ma_standard_sample_rate_32000, 12129 (ma_uint32)ma_standard_sample_rate_24000, 12130 (ma_uint32)ma_standard_sample_rate_22050, 12131 12132 (ma_uint32)ma_standard_sample_rate_88200, 12133 (ma_uint32)ma_standard_sample_rate_96000, 12134 (ma_uint32)ma_standard_sample_rate_176400, 12135 (ma_uint32)ma_standard_sample_rate_192000, 12136 12137 (ma_uint32)ma_standard_sample_rate_16000, 12138 (ma_uint32)ma_standard_sample_rate_11025, 12139 (ma_uint32)ma_standard_sample_rate_8000, 12140 12141 (ma_uint32)ma_standard_sample_rate_352800, 12142 (ma_uint32)ma_standard_sample_rate_384000 12143 }; 12144 12145 static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) 12146 { 12147 ma_uint32 iSampleRate; 12148 12149 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { 12150 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { 12151 return MA_TRUE; 12152 } 12153 } 12154 12155 /* Getting here means the sample rate is not supported. */ 12156 return MA_FALSE; 12157 } 12158 12159 12160 static ma_format g_maFormatPriorities[] = { 12161 ma_format_s16, /* Most common */ 12162 ma_format_f32, 12163 12164 /*ma_format_s24_32,*/ /* Clean alignment */ 12165 ma_format_s32, 12166 12167 ma_format_s24, /* Unclean alignment */ 12168 12169 ma_format_u8 /* Low quality */ 12170 }; 12171 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 12172 #pragma GCC diagnostic pop 12173 #endif 12174 12175 12176 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) 12177 { 12178 if (pMajor) { 12179 *pMajor = MA_VERSION_MAJOR; 12180 } 12181 12182 if (pMinor) { 12183 *pMinor = MA_VERSION_MINOR; 12184 } 12185 12186 if (pRevision) { 12187 *pRevision = MA_VERSION_REVISION; 12188 } 12189 } 12190 12191 MA_API const char* ma_version_string(void) 12192 { 12193 return MA_VERSION_STRING; 12194 } 12195 12196 12197 /****************************************************************************** 12198 12199 Standard Library Stuff 12200 12201 ******************************************************************************/ 12202 #ifndef MA_ASSERT 12203 #define MA_ASSERT(condition) assert(condition) 12204 #endif 12205 12206 #ifndef MA_MALLOC 12207 #define MA_MALLOC(sz) malloc((sz)) 12208 #endif 12209 #ifndef MA_REALLOC 12210 #define MA_REALLOC(p, sz) realloc((p), (sz)) 12211 #endif 12212 #ifndef MA_FREE 12213 #define MA_FREE(p) free((p)) 12214 #endif 12215 12216 static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) 12217 { 12218 if (p == NULL) { 12219 MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ 12220 return; 12221 } 12222 12223 if (sz > 0) { 12224 memset(p, 0, sz); 12225 } 12226 } 12227 12228 12229 #ifndef MA_ZERO_MEMORY 12230 #define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) 12231 #endif 12232 #ifndef MA_COPY_MEMORY 12233 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) 12234 #endif 12235 #ifndef MA_MOVE_MEMORY 12236 #define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) 12237 #endif 12238 12239 #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) 12240 12241 #define ma_countof(x) (sizeof(x) / sizeof(x[0])) 12242 #define ma_max(x, y) (((x) > (y)) ? (x) : (y)) 12243 #define ma_min(x, y) (((x) < (y)) ? (x) : (y)) 12244 #define ma_abs(x) (((x) > 0) ? (x) : -(x)) 12245 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) 12246 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) 12247 #define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) 12248 #define ma_align_64(x) ma_align(x, 8) 12249 12250 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) 12251 12252 static MA_INLINE double ma_sind(double x) 12253 { 12254 /* TODO: Implement custom sin(x). */ 12255 return sin(x); 12256 } 12257 12258 static MA_INLINE double ma_expd(double x) 12259 { 12260 /* TODO: Implement custom exp(x). */ 12261 return exp(x); 12262 } 12263 12264 static MA_INLINE double ma_logd(double x) 12265 { 12266 /* TODO: Implement custom log(x). */ 12267 return log(x); 12268 } 12269 12270 static MA_INLINE double ma_powd(double x, double y) 12271 { 12272 /* TODO: Implement custom pow(x, y). */ 12273 return pow(x, y); 12274 } 12275 12276 static MA_INLINE double ma_sqrtd(double x) 12277 { 12278 /* TODO: Implement custom sqrt(x). */ 12279 return sqrt(x); 12280 } 12281 12282 12283 static MA_INLINE float ma_rsqrtf(float x) 12284 { 12285 #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) 12286 { 12287 /* 12288 For SSE we can use RSQRTSS. 12289 12290 This Stack Overflow post suggests that compilers don't necessarily generate optimal code 12291 when using intrinsics: 12292 12293 https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper 12294 12295 I'm going to do something similar here, but a bit simpler. 12296 */ 12297 #if defined(__GNUC__) || defined(__clang__) 12298 { 12299 float result; 12300 __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); 12301 return result; 12302 } 12303 #else 12304 { 12305 return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); 12306 } 12307 #endif 12308 } 12309 #else 12310 { 12311 return 1 / (float)ma_sqrtd(x); 12312 } 12313 #endif 12314 } 12315 12316 12317 static MA_INLINE float ma_sinf(float x) 12318 { 12319 return (float)ma_sind((float)x); 12320 } 12321 12322 static MA_INLINE double ma_cosd(double x) 12323 { 12324 return ma_sind((MA_PI_D*0.5) - x); 12325 } 12326 12327 static MA_INLINE float ma_cosf(float x) 12328 { 12329 return (float)ma_cosd((float)x); 12330 } 12331 12332 static MA_INLINE double ma_log10d(double x) 12333 { 12334 return ma_logd(x) * 0.43429448190325182765; 12335 } 12336 12337 static MA_INLINE float ma_powf(float x, float y) 12338 { 12339 return (float)ma_powd((double)x, (double)y); 12340 } 12341 12342 static MA_INLINE float ma_log10f(float x) 12343 { 12344 return (float)ma_log10d((double)x); 12345 } 12346 12347 12348 static MA_INLINE double ma_degrees_to_radians(double degrees) 12349 { 12350 return degrees * 0.01745329252; 12351 } 12352 12353 static MA_INLINE double ma_radians_to_degrees(double radians) 12354 { 12355 return radians * 57.295779512896; 12356 } 12357 12358 static MA_INLINE float ma_degrees_to_radians_f(float degrees) 12359 { 12360 return degrees * 0.01745329252f; 12361 } 12362 12363 static MA_INLINE float ma_radians_to_degrees_f(float radians) 12364 { 12365 return radians * 57.295779512896f; 12366 } 12367 12368 12369 /* 12370 Return Values: 12371 0: Success 12372 22: EINVAL 12373 34: ERANGE 12374 12375 Not using symbolic constants for errors because I want to avoid #including errno.h 12376 12377 These are marked as no-inline because of some bad code generation by Clang. None of these functions 12378 are used in any performance-critical code within miniaudio. 12379 */ 12380 MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) 12381 { 12382 size_t i; 12383 12384 if (dst == 0) { 12385 return 22; 12386 } 12387 if (dstSizeInBytes == 0) { 12388 return 34; 12389 } 12390 if (src == 0) { 12391 dst[0] = '\0'; 12392 return 22; 12393 } 12394 12395 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { 12396 dst[i] = src[i]; 12397 } 12398 12399 if (i < dstSizeInBytes) { 12400 dst[i] = '\0'; 12401 return 0; 12402 } 12403 12404 dst[0] = '\0'; 12405 return 34; 12406 } 12407 12408 MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) 12409 { 12410 size_t i; 12411 12412 if (dst == 0) { 12413 return 22; 12414 } 12415 if (dstCap == 0) { 12416 return 34; 12417 } 12418 if (src == 0) { 12419 dst[0] = '\0'; 12420 return 22; 12421 } 12422 12423 for (i = 0; i < dstCap && src[i] != '\0'; ++i) { 12424 dst[i] = src[i]; 12425 } 12426 12427 if (i < dstCap) { 12428 dst[i] = '\0'; 12429 return 0; 12430 } 12431 12432 dst[0] = '\0'; 12433 return 34; 12434 } 12435 12436 12437 MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) 12438 { 12439 size_t maxcount; 12440 size_t i; 12441 12442 if (dst == 0) { 12443 return 22; 12444 } 12445 if (dstSizeInBytes == 0) { 12446 return 34; 12447 } 12448 if (src == 0) { 12449 dst[0] = '\0'; 12450 return 22; 12451 } 12452 12453 maxcount = count; 12454 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ 12455 maxcount = dstSizeInBytes - 1; 12456 } 12457 12458 for (i = 0; i < maxcount && src[i] != '\0'; ++i) { 12459 dst[i] = src[i]; 12460 } 12461 12462 if (src[i] == '\0' || i == count || count == ((size_t)-1)) { 12463 dst[i] = '\0'; 12464 return 0; 12465 } 12466 12467 dst[0] = '\0'; 12468 return 34; 12469 } 12470 12471 MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) 12472 { 12473 char* dstorig; 12474 12475 if (dst == 0) { 12476 return 22; 12477 } 12478 if (dstSizeInBytes == 0) { 12479 return 34; 12480 } 12481 if (src == 0) { 12482 dst[0] = '\0'; 12483 return 22; 12484 } 12485 12486 dstorig = dst; 12487 12488 while (dstSizeInBytes > 0 && dst[0] != '\0') { 12489 dst += 1; 12490 dstSizeInBytes -= 1; 12491 } 12492 12493 if (dstSizeInBytes == 0) { 12494 return 22; /* Unterminated. */ 12495 } 12496 12497 12498 while (dstSizeInBytes > 0 && src[0] != '\0') { 12499 *dst++ = *src++; 12500 dstSizeInBytes -= 1; 12501 } 12502 12503 if (dstSizeInBytes > 0) { 12504 dst[0] = '\0'; 12505 } else { 12506 dstorig[0] = '\0'; 12507 return 34; 12508 } 12509 12510 return 0; 12511 } 12512 12513 MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) 12514 { 12515 char* dstorig; 12516 12517 if (dst == 0) { 12518 return 22; 12519 } 12520 if (dstSizeInBytes == 0) { 12521 return 34; 12522 } 12523 if (src == 0) { 12524 return 22; 12525 } 12526 12527 dstorig = dst; 12528 12529 while (dstSizeInBytes > 0 && dst[0] != '\0') { 12530 dst += 1; 12531 dstSizeInBytes -= 1; 12532 } 12533 12534 if (dstSizeInBytes == 0) { 12535 return 22; /* Unterminated. */ 12536 } 12537 12538 12539 if (count == ((size_t)-1)) { /* _TRUNCATE */ 12540 count = dstSizeInBytes - 1; 12541 } 12542 12543 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { 12544 *dst++ = *src++; 12545 dstSizeInBytes -= 1; 12546 count -= 1; 12547 } 12548 12549 if (dstSizeInBytes > 0) { 12550 dst[0] = '\0'; 12551 } else { 12552 dstorig[0] = '\0'; 12553 return 34; 12554 } 12555 12556 return 0; 12557 } 12558 12559 MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) 12560 { 12561 int sign; 12562 unsigned int valueU; 12563 char* dstEnd; 12564 12565 if (dst == NULL || dstSizeInBytes == 0) { 12566 return 22; 12567 } 12568 if (radix < 2 || radix > 36) { 12569 dst[0] = '\0'; 12570 return 22; 12571 } 12572 12573 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ 12574 12575 if (value < 0) { 12576 valueU = -value; 12577 } else { 12578 valueU = value; 12579 } 12580 12581 dstEnd = dst; 12582 do 12583 { 12584 int remainder = valueU % radix; 12585 if (remainder > 9) { 12586 *dstEnd = (char)((remainder - 10) + 'a'); 12587 } else { 12588 *dstEnd = (char)(remainder + '0'); 12589 } 12590 12591 dstEnd += 1; 12592 dstSizeInBytes -= 1; 12593 valueU /= radix; 12594 } while (dstSizeInBytes > 0 && valueU > 0); 12595 12596 if (dstSizeInBytes == 0) { 12597 dst[0] = '\0'; 12598 return 22; /* Ran out of room in the output buffer. */ 12599 } 12600 12601 if (sign < 0) { 12602 *dstEnd++ = '-'; 12603 dstSizeInBytes -= 1; 12604 } 12605 12606 if (dstSizeInBytes == 0) { 12607 dst[0] = '\0'; 12608 return 22; /* Ran out of room in the output buffer. */ 12609 } 12610 12611 *dstEnd = '\0'; 12612 12613 12614 /* At this point the string will be reversed. */ 12615 dstEnd -= 1; 12616 while (dst < dstEnd) { 12617 char temp = *dst; 12618 *dst = *dstEnd; 12619 *dstEnd = temp; 12620 12621 dst += 1; 12622 dstEnd -= 1; 12623 } 12624 12625 return 0; 12626 } 12627 12628 MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) 12629 { 12630 if (str1 == str2) return 0; 12631 12632 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ 12633 if (str1 == NULL) return -1; 12634 if (str2 == NULL) return 1; 12635 12636 for (;;) { 12637 if (str1[0] == '\0') { 12638 break; 12639 } 12640 if (str1[0] != str2[0]) { 12641 break; 12642 } 12643 12644 str1 += 1; 12645 str2 += 1; 12646 } 12647 12648 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; 12649 } 12650 12651 MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) 12652 { 12653 int result; 12654 12655 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); 12656 if (result != 0) { 12657 return result; 12658 } 12659 12660 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); 12661 if (result != 0) { 12662 return result; 12663 } 12664 12665 return result; 12666 } 12667 12668 MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) 12669 { 12670 size_t sz; 12671 char* dst; 12672 12673 if (src == NULL) { 12674 return NULL; 12675 } 12676 12677 sz = strlen(src)+1; 12678 dst = (char*)ma_malloc(sz, pAllocationCallbacks); 12679 if (dst == NULL) { 12680 return NULL; 12681 } 12682 12683 ma_strcpy_s(dst, sz, src); 12684 12685 return dst; 12686 } 12687 12688 MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) 12689 { 12690 size_t sz = wcslen(src)+1; 12691 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); 12692 if (dst == NULL) { 12693 return NULL; 12694 } 12695 12696 ma_wcscpy_s(dst, sz, src); 12697 12698 return dst; 12699 } 12700 12701 12702 12703 #include <errno.h> 12704 static ma_result ma_result_from_errno(int e) 12705 { 12706 if (e == 0) { 12707 return MA_SUCCESS; 12708 } 12709 #ifdef EPERM 12710 else if (e == EPERM) { return MA_INVALID_OPERATION; } 12711 #endif 12712 #ifdef ENOENT 12713 else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } 12714 #endif 12715 #ifdef ESRCH 12716 else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } 12717 #endif 12718 #ifdef EINTR 12719 else if (e == EINTR) { return MA_INTERRUPT; } 12720 #endif 12721 #ifdef EIO 12722 else if (e == EIO) { return MA_IO_ERROR; } 12723 #endif 12724 #ifdef ENXIO 12725 else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } 12726 #endif 12727 #ifdef E2BIG 12728 else if (e == E2BIG) { return MA_INVALID_ARGS; } 12729 #endif 12730 #ifdef ENOEXEC 12731 else if (e == ENOEXEC) { return MA_INVALID_FILE; } 12732 #endif 12733 #ifdef EBADF 12734 else if (e == EBADF) { return MA_INVALID_FILE; } 12735 #endif 12736 #ifdef ECHILD 12737 else if (e == ECHILD) { return MA_ERROR; } 12738 #endif 12739 #ifdef EAGAIN 12740 else if (e == EAGAIN) { return MA_UNAVAILABLE; } 12741 #endif 12742 #ifdef ENOMEM 12743 else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } 12744 #endif 12745 #ifdef EACCES 12746 else if (e == EACCES) { return MA_ACCESS_DENIED; } 12747 #endif 12748 #ifdef EFAULT 12749 else if (e == EFAULT) { return MA_BAD_ADDRESS; } 12750 #endif 12751 #ifdef ENOTBLK 12752 else if (e == ENOTBLK) { return MA_ERROR; } 12753 #endif 12754 #ifdef EBUSY 12755 else if (e == EBUSY) { return MA_BUSY; } 12756 #endif 12757 #ifdef EEXIST 12758 else if (e == EEXIST) { return MA_ALREADY_EXISTS; } 12759 #endif 12760 #ifdef EXDEV 12761 else if (e == EXDEV) { return MA_ERROR; } 12762 #endif 12763 #ifdef ENODEV 12764 else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } 12765 #endif 12766 #ifdef ENOTDIR 12767 else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } 12768 #endif 12769 #ifdef EISDIR 12770 else if (e == EISDIR) { return MA_IS_DIRECTORY; } 12771 #endif 12772 #ifdef EINVAL 12773 else if (e == EINVAL) { return MA_INVALID_ARGS; } 12774 #endif 12775 #ifdef ENFILE 12776 else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } 12777 #endif 12778 #ifdef EMFILE 12779 else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } 12780 #endif 12781 #ifdef ENOTTY 12782 else if (e == ENOTTY) { return MA_INVALID_OPERATION; } 12783 #endif 12784 #ifdef ETXTBSY 12785 else if (e == ETXTBSY) { return MA_BUSY; } 12786 #endif 12787 #ifdef EFBIG 12788 else if (e == EFBIG) { return MA_TOO_BIG; } 12789 #endif 12790 #ifdef ENOSPC 12791 else if (e == ENOSPC) { return MA_NO_SPACE; } 12792 #endif 12793 #ifdef ESPIPE 12794 else if (e == ESPIPE) { return MA_BAD_SEEK; } 12795 #endif 12796 #ifdef EROFS 12797 else if (e == EROFS) { return MA_ACCESS_DENIED; } 12798 #endif 12799 #ifdef EMLINK 12800 else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } 12801 #endif 12802 #ifdef EPIPE 12803 else if (e == EPIPE) { return MA_BAD_PIPE; } 12804 #endif 12805 #ifdef EDOM 12806 else if (e == EDOM) { return MA_OUT_OF_RANGE; } 12807 #endif 12808 #ifdef ERANGE 12809 else if (e == ERANGE) { return MA_OUT_OF_RANGE; } 12810 #endif 12811 #ifdef EDEADLK 12812 else if (e == EDEADLK) { return MA_DEADLOCK; } 12813 #endif 12814 #ifdef ENAMETOOLONG 12815 else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } 12816 #endif 12817 #ifdef ENOLCK 12818 else if (e == ENOLCK) { return MA_ERROR; } 12819 #endif 12820 #ifdef ENOSYS 12821 else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } 12822 #endif 12823 #ifdef ENOTEMPTY 12824 else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } 12825 #endif 12826 #ifdef ELOOP 12827 else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } 12828 #endif 12829 #ifdef ENOMSG 12830 else if (e == ENOMSG) { return MA_NO_MESSAGE; } 12831 #endif 12832 #ifdef EIDRM 12833 else if (e == EIDRM) { return MA_ERROR; } 12834 #endif 12835 #ifdef ECHRNG 12836 else if (e == ECHRNG) { return MA_ERROR; } 12837 #endif 12838 #ifdef EL2NSYNC 12839 else if (e == EL2NSYNC) { return MA_ERROR; } 12840 #endif 12841 #ifdef EL3HLT 12842 else if (e == EL3HLT) { return MA_ERROR; } 12843 #endif 12844 #ifdef EL3RST 12845 else if (e == EL3RST) { return MA_ERROR; } 12846 #endif 12847 #ifdef ELNRNG 12848 else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } 12849 #endif 12850 #ifdef EUNATCH 12851 else if (e == EUNATCH) { return MA_ERROR; } 12852 #endif 12853 #ifdef ENOCSI 12854 else if (e == ENOCSI) { return MA_ERROR; } 12855 #endif 12856 #ifdef EL2HLT 12857 else if (e == EL2HLT) { return MA_ERROR; } 12858 #endif 12859 #ifdef EBADE 12860 else if (e == EBADE) { return MA_ERROR; } 12861 #endif 12862 #ifdef EBADR 12863 else if (e == EBADR) { return MA_ERROR; } 12864 #endif 12865 #ifdef EXFULL 12866 else if (e == EXFULL) { return MA_ERROR; } 12867 #endif 12868 #ifdef ENOANO 12869 else if (e == ENOANO) { return MA_ERROR; } 12870 #endif 12871 #ifdef EBADRQC 12872 else if (e == EBADRQC) { return MA_ERROR; } 12873 #endif 12874 #ifdef EBADSLT 12875 else if (e == EBADSLT) { return MA_ERROR; } 12876 #endif 12877 #ifdef EBFONT 12878 else if (e == EBFONT) { return MA_INVALID_FILE; } 12879 #endif 12880 #ifdef ENOSTR 12881 else if (e == ENOSTR) { return MA_ERROR; } 12882 #endif 12883 #ifdef ENODATA 12884 else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } 12885 #endif 12886 #ifdef ETIME 12887 else if (e == ETIME) { return MA_TIMEOUT; } 12888 #endif 12889 #ifdef ENOSR 12890 else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } 12891 #endif 12892 #ifdef ENONET 12893 else if (e == ENONET) { return MA_NO_NETWORK; } 12894 #endif 12895 #ifdef ENOPKG 12896 else if (e == ENOPKG) { return MA_ERROR; } 12897 #endif 12898 #ifdef EREMOTE 12899 else if (e == EREMOTE) { return MA_ERROR; } 12900 #endif 12901 #ifdef ENOLINK 12902 else if (e == ENOLINK) { return MA_ERROR; } 12903 #endif 12904 #ifdef EADV 12905 else if (e == EADV) { return MA_ERROR; } 12906 #endif 12907 #ifdef ESRMNT 12908 else if (e == ESRMNT) { return MA_ERROR; } 12909 #endif 12910 #ifdef ECOMM 12911 else if (e == ECOMM) { return MA_ERROR; } 12912 #endif 12913 #ifdef EPROTO 12914 else if (e == EPROTO) { return MA_ERROR; } 12915 #endif 12916 #ifdef EMULTIHOP 12917 else if (e == EMULTIHOP) { return MA_ERROR; } 12918 #endif 12919 #ifdef EDOTDOT 12920 else if (e == EDOTDOT) { return MA_ERROR; } 12921 #endif 12922 #ifdef EBADMSG 12923 else if (e == EBADMSG) { return MA_BAD_MESSAGE; } 12924 #endif 12925 #ifdef EOVERFLOW 12926 else if (e == EOVERFLOW) { return MA_TOO_BIG; } 12927 #endif 12928 #ifdef ENOTUNIQ 12929 else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } 12930 #endif 12931 #ifdef EBADFD 12932 else if (e == EBADFD) { return MA_ERROR; } 12933 #endif 12934 #ifdef EREMCHG 12935 else if (e == EREMCHG) { return MA_ERROR; } 12936 #endif 12937 #ifdef ELIBACC 12938 else if (e == ELIBACC) { return MA_ACCESS_DENIED; } 12939 #endif 12940 #ifdef ELIBBAD 12941 else if (e == ELIBBAD) { return MA_INVALID_FILE; } 12942 #endif 12943 #ifdef ELIBSCN 12944 else if (e == ELIBSCN) { return MA_INVALID_FILE; } 12945 #endif 12946 #ifdef ELIBMAX 12947 else if (e == ELIBMAX) { return MA_ERROR; } 12948 #endif 12949 #ifdef ELIBEXEC 12950 else if (e == ELIBEXEC) { return MA_ERROR; } 12951 #endif 12952 #ifdef EILSEQ 12953 else if (e == EILSEQ) { return MA_INVALID_DATA; } 12954 #endif 12955 #ifdef ERESTART 12956 else if (e == ERESTART) { return MA_ERROR; } 12957 #endif 12958 #ifdef ESTRPIPE 12959 else if (e == ESTRPIPE) { return MA_ERROR; } 12960 #endif 12961 #ifdef EUSERS 12962 else if (e == EUSERS) { return MA_ERROR; } 12963 #endif 12964 #ifdef ENOTSOCK 12965 else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } 12966 #endif 12967 #ifdef EDESTADDRREQ 12968 else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } 12969 #endif 12970 #ifdef EMSGSIZE 12971 else if (e == EMSGSIZE) { return MA_TOO_BIG; } 12972 #endif 12973 #ifdef EPROTOTYPE 12974 else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } 12975 #endif 12976 #ifdef ENOPROTOOPT 12977 else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } 12978 #endif 12979 #ifdef EPROTONOSUPPORT 12980 else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } 12981 #endif 12982 #ifdef ESOCKTNOSUPPORT 12983 else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } 12984 #endif 12985 #ifdef EOPNOTSUPP 12986 else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } 12987 #endif 12988 #ifdef EPFNOSUPPORT 12989 else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } 12990 #endif 12991 #ifdef EAFNOSUPPORT 12992 else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } 12993 #endif 12994 #ifdef EADDRINUSE 12995 else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } 12996 #endif 12997 #ifdef EADDRNOTAVAIL 12998 else if (e == EADDRNOTAVAIL) { return MA_ERROR; } 12999 #endif 13000 #ifdef ENETDOWN 13001 else if (e == ENETDOWN) { return MA_NO_NETWORK; } 13002 #endif 13003 #ifdef ENETUNREACH 13004 else if (e == ENETUNREACH) { return MA_NO_NETWORK; } 13005 #endif 13006 #ifdef ENETRESET 13007 else if (e == ENETRESET) { return MA_NO_NETWORK; } 13008 #endif 13009 #ifdef ECONNABORTED 13010 else if (e == ECONNABORTED) { return MA_NO_NETWORK; } 13011 #endif 13012 #ifdef ECONNRESET 13013 else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } 13014 #endif 13015 #ifdef ENOBUFS 13016 else if (e == ENOBUFS) { return MA_NO_SPACE; } 13017 #endif 13018 #ifdef EISCONN 13019 else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } 13020 #endif 13021 #ifdef ENOTCONN 13022 else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } 13023 #endif 13024 #ifdef ESHUTDOWN 13025 else if (e == ESHUTDOWN) { return MA_ERROR; } 13026 #endif 13027 #ifdef ETOOMANYREFS 13028 else if (e == ETOOMANYREFS) { return MA_ERROR; } 13029 #endif 13030 #ifdef ETIMEDOUT 13031 else if (e == ETIMEDOUT) { return MA_TIMEOUT; } 13032 #endif 13033 #ifdef ECONNREFUSED 13034 else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } 13035 #endif 13036 #ifdef EHOSTDOWN 13037 else if (e == EHOSTDOWN) { return MA_NO_HOST; } 13038 #endif 13039 #ifdef EHOSTUNREACH 13040 else if (e == EHOSTUNREACH) { return MA_NO_HOST; } 13041 #endif 13042 #ifdef EALREADY 13043 else if (e == EALREADY) { return MA_IN_PROGRESS; } 13044 #endif 13045 #ifdef EINPROGRESS 13046 else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } 13047 #endif 13048 #ifdef ESTALE 13049 else if (e == ESTALE) { return MA_INVALID_FILE; } 13050 #endif 13051 #ifdef EUCLEAN 13052 else if (e == EUCLEAN) { return MA_ERROR; } 13053 #endif 13054 #ifdef ENOTNAM 13055 else if (e == ENOTNAM) { return MA_ERROR; } 13056 #endif 13057 #ifdef ENAVAIL 13058 else if (e == ENAVAIL) { return MA_ERROR; } 13059 #endif 13060 #ifdef EISNAM 13061 else if (e == EISNAM) { return MA_ERROR; } 13062 #endif 13063 #ifdef EREMOTEIO 13064 else if (e == EREMOTEIO) { return MA_IO_ERROR; } 13065 #endif 13066 #ifdef EDQUOT 13067 else if (e == EDQUOT) { return MA_NO_SPACE; } 13068 #endif 13069 #ifdef ENOMEDIUM 13070 else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } 13071 #endif 13072 #ifdef EMEDIUMTYPE 13073 else if (e == EMEDIUMTYPE) { return MA_ERROR; } 13074 #endif 13075 #ifdef ECANCELED 13076 else if (e == ECANCELED) { return MA_CANCELLED; } 13077 #endif 13078 #ifdef ENOKEY 13079 else if (e == ENOKEY) { return MA_ERROR; } 13080 #endif 13081 #ifdef EKEYEXPIRED 13082 else if (e == EKEYEXPIRED) { return MA_ERROR; } 13083 #endif 13084 #ifdef EKEYREVOKED 13085 else if (e == EKEYREVOKED) { return MA_ERROR; } 13086 #endif 13087 #ifdef EKEYREJECTED 13088 else if (e == EKEYREJECTED) { return MA_ERROR; } 13089 #endif 13090 #ifdef EOWNERDEAD 13091 else if (e == EOWNERDEAD) { return MA_ERROR; } 13092 #endif 13093 #ifdef ENOTRECOVERABLE 13094 else if (e == ENOTRECOVERABLE) { return MA_ERROR; } 13095 #endif 13096 #ifdef ERFKILL 13097 else if (e == ERFKILL) { return MA_ERROR; } 13098 #endif 13099 #ifdef EHWPOISON 13100 else if (e == EHWPOISON) { return MA_ERROR; } 13101 #endif 13102 else { 13103 return MA_ERROR; 13104 } 13105 } 13106 13107 MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) 13108 { 13109 #if defined(_MSC_VER) && _MSC_VER >= 1400 13110 errno_t err; 13111 #endif 13112 13113 if (ppFile != NULL) { 13114 *ppFile = NULL; /* Safety. */ 13115 } 13116 13117 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { 13118 return MA_INVALID_ARGS; 13119 } 13120 13121 #if defined(_MSC_VER) && _MSC_VER >= 1400 13122 err = fopen_s(ppFile, pFilePath, pOpenMode); 13123 if (err != 0) { 13124 return ma_result_from_errno(err); 13125 } 13126 #else 13127 #if defined(_WIN32) || defined(__APPLE__) 13128 *ppFile = fopen(pFilePath, pOpenMode); 13129 #else 13130 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) 13131 *ppFile = fopen64(pFilePath, pOpenMode); 13132 #else 13133 *ppFile = fopen(pFilePath, pOpenMode); 13134 #endif 13135 #endif 13136 if (*ppFile == NULL) { 13137 ma_result result = ma_result_from_errno(errno); 13138 if (result == MA_SUCCESS) { 13139 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ 13140 } 13141 13142 return result; 13143 } 13144 #endif 13145 13146 return MA_SUCCESS; 13147 } 13148 13149 13150 13151 /* 13152 _wfopen() isn't always available in all compilation environments. 13153 13154 * Windows only. 13155 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). 13156 * MinGW-64 (both 32- and 64-bit) seems to support it. 13157 * MinGW wraps it in !defined(__STRICT_ANSI__). 13158 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). 13159 13160 This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() 13161 fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. 13162 */ 13163 #if defined(_WIN32) 13164 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) 13165 #define MA_HAS_WFOPEN 13166 #endif 13167 #endif 13168 13169 MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) 13170 { 13171 if (ppFile != NULL) { 13172 *ppFile = NULL; /* Safety. */ 13173 } 13174 13175 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { 13176 return MA_INVALID_ARGS; 13177 } 13178 13179 #if defined(MA_HAS_WFOPEN) 13180 { 13181 /* Use _wfopen() on Windows. */ 13182 #if defined(_MSC_VER) && _MSC_VER >= 1400 13183 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); 13184 if (err != 0) { 13185 return ma_result_from_errno(err); 13186 } 13187 #else 13188 *ppFile = _wfopen(pFilePath, pOpenMode); 13189 if (*ppFile == NULL) { 13190 return ma_result_from_errno(errno); 13191 } 13192 #endif 13193 (void)pAllocationCallbacks; 13194 } 13195 #else 13196 /* 13197 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can 13198 think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for 13199 maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. 13200 */ 13201 { 13202 mbstate_t mbs; 13203 size_t lenMB; 13204 const wchar_t* pFilePathTemp = pFilePath; 13205 char* pFilePathMB = NULL; 13206 char pOpenModeMB[32] = {0}; 13207 13208 /* Get the length first. */ 13209 MA_ZERO_OBJECT(&mbs); 13210 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); 13211 if (lenMB == (size_t)-1) { 13212 return ma_result_from_errno(errno); 13213 } 13214 13215 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); 13216 if (pFilePathMB == NULL) { 13217 return MA_OUT_OF_MEMORY; 13218 } 13219 13220 pFilePathTemp = pFilePath; 13221 MA_ZERO_OBJECT(&mbs); 13222 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); 13223 13224 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ 13225 { 13226 size_t i = 0; 13227 for (;;) { 13228 if (pOpenMode[i] == 0) { 13229 pOpenModeMB[i] = '\0'; 13230 break; 13231 } 13232 13233 pOpenModeMB[i] = (char)pOpenMode[i]; 13234 i += 1; 13235 } 13236 } 13237 13238 *ppFile = fopen(pFilePathMB, pOpenModeMB); 13239 13240 ma_free(pFilePathMB, pAllocationCallbacks); 13241 } 13242 13243 if (*ppFile == NULL) { 13244 return MA_ERROR; 13245 } 13246 #endif 13247 13248 return MA_SUCCESS; 13249 } 13250 13251 13252 13253 static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) 13254 { 13255 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX 13256 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); 13257 #else 13258 while (sizeInBytes > 0) { 13259 ma_uint64 bytesToCopyNow = sizeInBytes; 13260 if (bytesToCopyNow > MA_SIZE_MAX) { 13261 bytesToCopyNow = MA_SIZE_MAX; 13262 } 13263 13264 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ 13265 13266 sizeInBytes -= bytesToCopyNow; 13267 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); 13268 src = (const void*)((const ma_uint8*)src + bytesToCopyNow); 13269 } 13270 #endif 13271 } 13272 13273 static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) 13274 { 13275 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX 13276 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); 13277 #else 13278 while (sizeInBytes > 0) { 13279 ma_uint64 bytesToZeroNow = sizeInBytes; 13280 if (bytesToZeroNow > MA_SIZE_MAX) { 13281 bytesToZeroNow = MA_SIZE_MAX; 13282 } 13283 13284 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ 13285 13286 sizeInBytes -= bytesToZeroNow; 13287 dst = (void*)((ma_uint8*)dst + bytesToZeroNow); 13288 } 13289 #endif 13290 } 13291 13292 13293 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ 13294 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) 13295 { 13296 x--; 13297 x |= x >> 1; 13298 x |= x >> 2; 13299 x |= x >> 4; 13300 x |= x >> 8; 13301 x |= x >> 16; 13302 x++; 13303 13304 return x; 13305 } 13306 13307 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) 13308 { 13309 return ma_next_power_of_2(x) >> 1; 13310 } 13311 13312 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) 13313 { 13314 unsigned int prev = ma_prev_power_of_2(x); 13315 unsigned int next = ma_next_power_of_2(x); 13316 if ((next - x) > (x - prev)) { 13317 return prev; 13318 } else { 13319 return next; 13320 } 13321 } 13322 13323 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) 13324 { 13325 unsigned int count = 0; 13326 while (x != 0) { 13327 if (x & 1) { 13328 count += 1; 13329 } 13330 13331 x = x >> 1; 13332 } 13333 13334 return count; 13335 } 13336 13337 13338 13339 /************************************************************************************************************************************************************** 13340 13341 Allocation Callbacks 13342 13343 **************************************************************************************************************************************************************/ 13344 static void* ma__malloc_default(size_t sz, void* pUserData) 13345 { 13346 (void)pUserData; 13347 return MA_MALLOC(sz); 13348 } 13349 13350 static void* ma__realloc_default(void* p, size_t sz, void* pUserData) 13351 { 13352 (void)pUserData; 13353 return MA_REALLOC(p, sz); 13354 } 13355 13356 static void ma__free_default(void* p, void* pUserData) 13357 { 13358 (void)pUserData; 13359 MA_FREE(p); 13360 } 13361 13362 static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) 13363 { 13364 ma_allocation_callbacks callbacks; 13365 callbacks.pUserData = NULL; 13366 callbacks.onMalloc = ma__malloc_default; 13367 callbacks.onRealloc = ma__realloc_default; 13368 callbacks.onFree = ma__free_default; 13369 13370 return callbacks; 13371 } 13372 13373 static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) 13374 { 13375 if (pDst == NULL) { 13376 return MA_INVALID_ARGS; 13377 } 13378 13379 if (pSrc == NULL) { 13380 *pDst = ma_allocation_callbacks_init_default(); 13381 } else { 13382 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { 13383 *pDst = ma_allocation_callbacks_init_default(); 13384 } else { 13385 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { 13386 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ 13387 } else { 13388 *pDst = *pSrc; 13389 } 13390 } 13391 } 13392 13393 return MA_SUCCESS; 13394 } 13395 13396 13397 13398 13399 /************************************************************************************************************************************************************** 13400 13401 Logging 13402 13403 **************************************************************************************************************************************************************/ 13404 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) 13405 { 13406 switch (logLevel) 13407 { 13408 case MA_LOG_LEVEL_DEBUG: return "DEBUG"; 13409 case MA_LOG_LEVEL_INFO: return "INFO"; 13410 case MA_LOG_LEVEL_WARNING: return "WARNING"; 13411 case MA_LOG_LEVEL_ERROR: return "ERROR"; 13412 default: return "ERROR"; 13413 } 13414 } 13415 13416 #if defined(MA_DEBUG_OUTPUT) 13417 #if defined(MA_ANDROID) 13418 #include <android/log.h> 13419 #endif 13420 13421 /* Customize this to use a specific tag in __android_log_print() for debug output messages. */ 13422 #ifndef MA_ANDROID_LOG_TAG 13423 #define MA_ANDROID_LOG_TAG "miniaudio" 13424 #endif 13425 13426 void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) 13427 { 13428 (void)pUserData; 13429 13430 /* Special handling for some platforms. */ 13431 #if defined(MA_ANDROID) 13432 { 13433 /* Android. */ 13434 __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); 13435 } 13436 #else 13437 { 13438 /* Everything else. */ 13439 printf("%s: %s", ma_log_level_to_string(level), pMessage); 13440 } 13441 #endif 13442 } 13443 #endif 13444 13445 MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) 13446 { 13447 ma_log_callback callback; 13448 13449 MA_ZERO_OBJECT(&callback); 13450 callback.onLog = onLog; 13451 callback.pUserData = pUserData; 13452 13453 return callback; 13454 } 13455 13456 13457 MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) 13458 { 13459 if (pLog == NULL) { 13460 return MA_INVALID_ARGS; 13461 } 13462 13463 MA_ZERO_OBJECT(pLog); 13464 ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); 13465 13466 /* We need a mutex for thread safety. */ 13467 #ifndef MA_NO_THREADING 13468 { 13469 ma_result result = ma_mutex_init(&pLog->lock); 13470 if (result != MA_SUCCESS) { 13471 return result; 13472 } 13473 } 13474 #endif 13475 13476 /* If we're using debug output, enable it. */ 13477 #if defined(MA_DEBUG_OUTPUT) 13478 { 13479 ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ 13480 } 13481 #endif 13482 13483 return MA_SUCCESS; 13484 } 13485 13486 MA_API void ma_log_uninit(ma_log* pLog) 13487 { 13488 if (pLog == NULL) { 13489 return; 13490 } 13491 13492 #ifndef MA_NO_THREADING 13493 ma_mutex_uninit(&pLog->lock); 13494 #endif 13495 } 13496 13497 static void ma_log_lock(ma_log* pLog) 13498 { 13499 #ifndef MA_NO_THREADING 13500 ma_mutex_lock(&pLog->lock); 13501 #else 13502 (void)pLog; 13503 #endif 13504 } 13505 13506 static void ma_log_unlock(ma_log* pLog) 13507 { 13508 #ifndef MA_NO_THREADING 13509 ma_mutex_unlock(&pLog->lock); 13510 #else 13511 (void)pLog; 13512 #endif 13513 } 13514 13515 MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) 13516 { 13517 ma_result result = MA_SUCCESS; 13518 13519 if (pLog == NULL || callback.onLog == NULL) { 13520 return MA_INVALID_ARGS; 13521 } 13522 13523 ma_log_lock(pLog); 13524 { 13525 if (pLog->callbackCount == ma_countof(pLog->callbacks)) { 13526 result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ 13527 } else { 13528 pLog->callbacks[pLog->callbackCount] = callback; 13529 pLog->callbackCount += 1; 13530 } 13531 } 13532 ma_log_unlock(pLog); 13533 13534 return result; 13535 } 13536 13537 MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) 13538 { 13539 if (pLog == NULL) { 13540 return MA_INVALID_ARGS; 13541 } 13542 13543 ma_log_lock(pLog); 13544 { 13545 ma_uint32 iLog; 13546 for (iLog = 0; iLog < pLog->callbackCount; ) { 13547 if (pLog->callbacks[iLog].onLog == callback.onLog) { 13548 /* Found. Move everything down a slot. */ 13549 ma_uint32 jLog; 13550 for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { 13551 pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; 13552 } 13553 13554 pLog->callbackCount -= 1; 13555 } else { 13556 /* Not found. */ 13557 iLog += 1; 13558 } 13559 } 13560 } 13561 ma_log_unlock(pLog); 13562 13563 return MA_SUCCESS; 13564 } 13565 13566 MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) 13567 { 13568 if (pLog == NULL || pMessage == NULL) { 13569 return MA_INVALID_ARGS; 13570 } 13571 13572 ma_log_lock(pLog); 13573 { 13574 ma_uint32 iLog; 13575 for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { 13576 if (pLog->callbacks[iLog].onLog) { 13577 pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); 13578 } 13579 } 13580 } 13581 ma_log_unlock(pLog); 13582 13583 return MA_SUCCESS; 13584 } 13585 13586 13587 /* 13588 We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a 13589 logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). 13590 */ 13591 #if defined(_MSC_VER) && _MSC_VER < 1900 13592 static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) 13593 { 13594 #if _MSC_VER > 1200 13595 return _vscprintf(format, args); 13596 #else 13597 int result; 13598 char* pTempBuffer = NULL; 13599 size_t tempBufferCap = 1024; 13600 13601 if (format == NULL) { 13602 errno = EINVAL; 13603 return -1; 13604 } 13605 13606 for (;;) { 13607 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); 13608 if (pNewTempBuffer == NULL) { 13609 ma_free(pTempBuffer, pAllocationCallbacks); 13610 errno = ENOMEM; 13611 return -1; /* Out of memory. */ 13612 } 13613 13614 pTempBuffer = pNewTempBuffer; 13615 13616 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); 13617 ma_free(pTempBuffer, NULL); 13618 13619 if (result != -1) { 13620 break; /* Got it. */ 13621 } 13622 13623 /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ 13624 tempBufferCap *= 2; 13625 } 13626 13627 return result; 13628 #endif 13629 } 13630 #endif 13631 13632 MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) 13633 { 13634 if (pLog == NULL || pFormat == NULL) { 13635 return MA_INVALID_ARGS; 13636 } 13637 13638 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) 13639 { 13640 ma_result result; 13641 int length; 13642 char pFormattedMessageStack[1024]; 13643 char* pFormattedMessageHeap = NULL; 13644 13645 /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ 13646 length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); 13647 if (length < 0) { 13648 return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ 13649 } 13650 13651 if ((size_t)length < sizeof(pFormattedMessageStack)) { 13652 /* The string was written to the stack. */ 13653 result = ma_log_post(pLog, level, pFormattedMessageStack); 13654 } else { 13655 /* The stack buffer was too small, try the heap. */ 13656 pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); 13657 if (pFormattedMessageHeap == NULL) { 13658 return MA_OUT_OF_MEMORY; 13659 } 13660 13661 length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); 13662 if (length < 0) { 13663 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); 13664 return MA_INVALID_OPERATION; 13665 } 13666 13667 result = ma_log_post(pLog, level, pFormattedMessageHeap); 13668 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); 13669 } 13670 13671 return result; 13672 } 13673 #else 13674 { 13675 /* 13676 Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll 13677 need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing 13678 a fixed sized stack allocated buffer. 13679 */ 13680 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ 13681 { 13682 ma_result result; 13683 int formattedLen; 13684 char* pFormattedMessage = NULL; 13685 va_list args2; 13686 13687 #if _MSC_VER >= 1800 13688 { 13689 va_copy(args2, args); 13690 } 13691 #else 13692 { 13693 args2 = args; 13694 } 13695 #endif 13696 13697 formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); 13698 va_end(args2); 13699 13700 if (formattedLen <= 0) { 13701 return MA_INVALID_OPERATION; 13702 } 13703 13704 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); 13705 if (pFormattedMessage == NULL) { 13706 return MA_OUT_OF_MEMORY; 13707 } 13708 13709 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ 13710 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ 13711 { 13712 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); 13713 } 13714 #else 13715 { 13716 vsprintf(pFormattedMessage, pFormat, args); 13717 } 13718 #endif 13719 13720 result = ma_log_post(pLog, level, pFormattedMessage); 13721 ma_free(pFormattedMessage, &pLog->allocationCallbacks); 13722 13723 return result; 13724 } 13725 #else 13726 { 13727 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ 13728 (void)level; 13729 (void)args; 13730 13731 return MA_INVALID_OPERATION; 13732 } 13733 #endif 13734 } 13735 #endif 13736 } 13737 13738 MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) 13739 { 13740 ma_result result; 13741 va_list args; 13742 13743 if (pLog == NULL || pFormat == NULL) { 13744 return MA_INVALID_ARGS; 13745 } 13746 13747 va_start(args, pFormat); 13748 { 13749 result = ma_log_postv(pLog, level, pFormat, args); 13750 } 13751 va_end(args); 13752 13753 return result; 13754 } 13755 13756 13757 13758 static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) 13759 { 13760 return (ma_uint8)(ma_clamp(x, -128, 127) + 128); 13761 } 13762 13763 static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) 13764 { 13765 return (ma_int16)ma_clamp(x, -32768, 32767); 13766 } 13767 13768 static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) 13769 { 13770 return (ma_int64)ma_clamp(x, -8388608, 8388607); 13771 } 13772 13773 static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) 13774 { 13775 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ 13776 ma_int64 clipMin; 13777 ma_int64 clipMax; 13778 clipMin = -((ma_int64)2147483647 + 1); 13779 clipMax = (ma_int64)2147483647; 13780 13781 return (ma_int32)ma_clamp(x, clipMin, clipMax); 13782 } 13783 13784 static MA_INLINE float ma_clip_f32(float x) 13785 { 13786 if (x < -1) return -1; 13787 if (x > +1) return +1; 13788 return x; 13789 } 13790 13791 13792 static MA_INLINE float ma_mix_f32(float x, float y, float a) 13793 { 13794 return x*(1-a) + y*a; 13795 } 13796 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) 13797 { 13798 float r0 = (y - x); 13799 float r1 = r0*a; 13800 return x + r1; 13801 /*return x + (y - x)*a;*/ 13802 } 13803 13804 #if defined(MA_SUPPORT_SSE2) 13805 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) 13806 { 13807 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); 13808 } 13809 #endif 13810 #if defined(MA_SUPPORT_AVX2) 13811 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) 13812 { 13813 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); 13814 } 13815 #endif 13816 #if defined(MA_SUPPORT_NEON) 13817 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) 13818 { 13819 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); 13820 } 13821 #endif 13822 13823 13824 static MA_INLINE double ma_mix_f64(double x, double y, double a) 13825 { 13826 return x*(1-a) + y*a; 13827 } 13828 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) 13829 { 13830 return x + (y - x)*a; 13831 } 13832 13833 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) 13834 { 13835 return lo + x*(hi-lo); 13836 } 13837 13838 13839 /* 13840 Greatest common factor using Euclid's algorithm iteratively. 13841 */ 13842 static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) 13843 { 13844 for (;;) { 13845 if (b == 0) { 13846 break; 13847 } else { 13848 ma_uint32 t = a; 13849 a = b; 13850 b = t % a; 13851 } 13852 } 13853 13854 return a; 13855 } 13856 13857 13858 static ma_uint32 ma_ffs_32(ma_uint32 x) 13859 { 13860 ma_uint32 i; 13861 13862 /* Just a naive implementation just to get things working for now. Will optimize this later. */ 13863 for (i = 0; i < 32; i += 1) { 13864 if ((x & (1 << i)) != 0) { 13865 return i; 13866 } 13867 } 13868 13869 return i; 13870 } 13871 13872 static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) 13873 { 13874 return (ma_int16)(x * (1 << 8)); 13875 } 13876 13877 13878 13879 /* 13880 Random Number Generation 13881 13882 miniaudio uses the LCG random number generation algorithm. This is good enough for audio. 13883 13884 Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across 13885 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for 13886 miniaudio's purposes. 13887 */ 13888 #ifndef MA_DEFAULT_LCG_SEED 13889 #define MA_DEFAULT_LCG_SEED 4321 13890 #endif 13891 13892 #define MA_LCG_M 2147483647 13893 #define MA_LCG_A 48271 13894 #define MA_LCG_C 0 13895 13896 static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ 13897 13898 static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) 13899 { 13900 MA_ASSERT(pLCG != NULL); 13901 pLCG->state = seed; 13902 } 13903 13904 static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) 13905 { 13906 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; 13907 return pLCG->state; 13908 } 13909 13910 static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) 13911 { 13912 return (ma_uint32)ma_lcg_rand_s32(pLCG); 13913 } 13914 13915 static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) 13916 { 13917 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); 13918 } 13919 13920 static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) 13921 { 13922 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; 13923 } 13924 13925 static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) 13926 { 13927 return (float)ma_lcg_rand_f64(pLCG); 13928 } 13929 13930 static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) 13931 { 13932 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); 13933 } 13934 13935 static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) 13936 { 13937 if (lo == hi) { 13938 return lo; 13939 } 13940 13941 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); 13942 } 13943 13944 13945 13946 static MA_INLINE void ma_seed(ma_int32 seed) 13947 { 13948 ma_lcg_seed(&g_maLCG, seed); 13949 } 13950 13951 static MA_INLINE ma_int32 ma_rand_s32(void) 13952 { 13953 return ma_lcg_rand_s32(&g_maLCG); 13954 } 13955 13956 static MA_INLINE ma_uint32 ma_rand_u32(void) 13957 { 13958 return ma_lcg_rand_u32(&g_maLCG); 13959 } 13960 13961 static MA_INLINE double ma_rand_f64(void) 13962 { 13963 return ma_lcg_rand_f64(&g_maLCG); 13964 } 13965 13966 static MA_INLINE float ma_rand_f32(void) 13967 { 13968 return ma_lcg_rand_f32(&g_maLCG); 13969 } 13970 13971 static MA_INLINE float ma_rand_range_f32(float lo, float hi) 13972 { 13973 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); 13974 } 13975 13976 static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) 13977 { 13978 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); 13979 } 13980 13981 13982 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) 13983 { 13984 return ma_rand_range_f32(ditherMin, ditherMax); 13985 } 13986 13987 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) 13988 { 13989 float a = ma_rand_range_f32(ditherMin, 0); 13990 float b = ma_rand_range_f32(0, ditherMax); 13991 return a + b; 13992 } 13993 13994 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) 13995 { 13996 if (ditherMode == ma_dither_mode_rectangle) { 13997 return ma_dither_f32_rectangle(ditherMin, ditherMax); 13998 } 13999 if (ditherMode == ma_dither_mode_triangle) { 14000 return ma_dither_f32_triangle(ditherMin, ditherMax); 14001 } 14002 14003 return 0; 14004 } 14005 14006 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) 14007 { 14008 if (ditherMode == ma_dither_mode_rectangle) { 14009 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); 14010 return a; 14011 } 14012 if (ditherMode == ma_dither_mode_triangle) { 14013 ma_int32 a = ma_rand_range_s32(ditherMin, 0); 14014 ma_int32 b = ma_rand_range_s32(0, ditherMax); 14015 return a + b; 14016 } 14017 14018 return 0; 14019 } 14020 14021 14022 /************************************************************************************************************************************************************** 14023 14024 Atomics 14025 14026 **************************************************************************************************************************************************************/ 14027 /* ma_atomic.h begin */ 14028 #ifndef ma_atomic_h 14029 #if defined(__cplusplus) 14030 extern "C" { 14031 #endif 14032 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 14033 #pragma GCC diagnostic push 14034 #pragma GCC diagnostic ignored "-Wlong-long" 14035 #if defined(__clang__) 14036 #pragma GCC diagnostic ignored "-Wc++11-long-long" 14037 #endif 14038 #endif 14039 typedef int ma_atomic_memory_order; 14040 #define MA_ATOMIC_HAS_8 14041 #define MA_ATOMIC_HAS_16 14042 #define MA_ATOMIC_HAS_32 14043 #define MA_ATOMIC_HAS_64 14044 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) 14045 #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ 14046 ma_atomicType result; \ 14047 switch (order) \ 14048 { \ 14049 case ma_atomic_memory_order_relaxed: \ 14050 { \ 14051 result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ 14052 } break; \ 14053 case ma_atomic_memory_order_consume: \ 14054 case ma_atomic_memory_order_acquire: \ 14055 { \ 14056 result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ 14057 } break; \ 14058 case ma_atomic_memory_order_release: \ 14059 { \ 14060 result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ 14061 } break; \ 14062 case ma_atomic_memory_order_acq_rel: \ 14063 case ma_atomic_memory_order_seq_cst: \ 14064 default: \ 14065 { \ 14066 result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ 14067 } break; \ 14068 } \ 14069 return result; 14070 #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ 14071 ma_atomicType result; \ 14072 switch (order) \ 14073 { \ 14074 case ma_atomic_memory_order_relaxed: \ 14075 { \ 14076 result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ 14077 } break; \ 14078 case ma_atomic_memory_order_consume: \ 14079 case ma_atomic_memory_order_acquire: \ 14080 { \ 14081 result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ 14082 } break; \ 14083 case ma_atomic_memory_order_release: \ 14084 { \ 14085 result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ 14086 } break; \ 14087 case ma_atomic_memory_order_acq_rel: \ 14088 case ma_atomic_memory_order_seq_cst: \ 14089 default: \ 14090 { \ 14091 result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ 14092 } break; \ 14093 } \ 14094 return result; 14095 #define ma_atomic_memory_order_relaxed 0 14096 #define ma_atomic_memory_order_consume 1 14097 #define ma_atomic_memory_order_acquire 2 14098 #define ma_atomic_memory_order_release 3 14099 #define ma_atomic_memory_order_acq_rel 4 14100 #define ma_atomic_memory_order_seq_cst 5 14101 #if _MSC_VER < 1600 && defined(MA_X86) 14102 #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY 14103 #endif 14104 #if _MSC_VER < 1600 14105 #undef MA_ATOMIC_HAS_8 14106 #undef MA_ATOMIC_HAS_16 14107 #endif 14108 #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) 14109 #include <intrin.h> 14110 #endif 14111 #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) 14112 #if defined(MA_ATOMIC_HAS_8) 14113 static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) 14114 { 14115 ma_uint8 result = 0; 14116 __asm { 14117 mov ecx, dst 14118 mov al, expected 14119 mov dl, desired 14120 lock cmpxchg [ecx], dl 14121 mov result, al 14122 } 14123 return result; 14124 } 14125 #endif 14126 #if defined(MA_ATOMIC_HAS_16) 14127 static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) 14128 { 14129 ma_uint16 result = 0; 14130 __asm { 14131 mov ecx, dst 14132 mov ax, expected 14133 mov dx, desired 14134 lock cmpxchg [ecx], dx 14135 mov result, ax 14136 } 14137 return result; 14138 } 14139 #endif 14140 #if defined(MA_ATOMIC_HAS_32) 14141 static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) 14142 { 14143 ma_uint32 result = 0; 14144 __asm { 14145 mov ecx, dst 14146 mov eax, expected 14147 mov edx, desired 14148 lock cmpxchg [ecx], edx 14149 mov result, eax 14150 } 14151 return result; 14152 } 14153 #endif 14154 #if defined(MA_ATOMIC_HAS_64) 14155 static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) 14156 { 14157 ma_uint32 resultEAX = 0; 14158 ma_uint32 resultEDX = 0; 14159 __asm { 14160 mov esi, dst 14161 mov eax, dword ptr expected 14162 mov edx, dword ptr expected + 4 14163 mov ebx, dword ptr desired 14164 mov ecx, dword ptr desired + 4 14165 lock cmpxchg8b qword ptr [esi] 14166 mov resultEAX, eax 14167 mov resultEDX, edx 14168 } 14169 return ((ma_uint64)resultEDX << 32) | resultEAX; 14170 } 14171 #endif 14172 #else 14173 #if defined(MA_ATOMIC_HAS_8) 14174 #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) 14175 #endif 14176 #if defined(MA_ATOMIC_HAS_16) 14177 #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) 14178 #endif 14179 #if defined(MA_ATOMIC_HAS_32) 14180 #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) 14181 #endif 14182 #if defined(MA_ATOMIC_HAS_64) 14183 #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) 14184 #endif 14185 #endif 14186 #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) 14187 #if defined(MA_ATOMIC_HAS_8) 14188 static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14189 { 14190 ma_uint8 result = 0; 14191 (void)order; 14192 __asm { 14193 mov ecx, dst 14194 mov al, src 14195 lock xchg [ecx], al 14196 mov result, al 14197 } 14198 return result; 14199 } 14200 #endif 14201 #if defined(MA_ATOMIC_HAS_16) 14202 static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14203 { 14204 ma_uint16 result = 0; 14205 (void)order; 14206 __asm { 14207 mov ecx, dst 14208 mov ax, src 14209 lock xchg [ecx], ax 14210 mov result, ax 14211 } 14212 return result; 14213 } 14214 #endif 14215 #if defined(MA_ATOMIC_HAS_32) 14216 static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14217 { 14218 ma_uint32 result = 0; 14219 (void)order; 14220 __asm { 14221 mov ecx, dst 14222 mov eax, src 14223 lock xchg [ecx], eax 14224 mov result, eax 14225 } 14226 return result; 14227 } 14228 #endif 14229 #else 14230 #if defined(MA_ATOMIC_HAS_8) 14231 static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14232 { 14233 #if defined(MA_ARM) 14234 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); 14235 #else 14236 (void)order; 14237 return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); 14238 #endif 14239 } 14240 #endif 14241 #if defined(MA_ATOMIC_HAS_16) 14242 static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14243 { 14244 #if defined(MA_ARM) 14245 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); 14246 #else 14247 (void)order; 14248 return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); 14249 #endif 14250 } 14251 #endif 14252 #if defined(MA_ATOMIC_HAS_32) 14253 static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14254 { 14255 #if defined(MA_ARM) 14256 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); 14257 #else 14258 (void)order; 14259 return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); 14260 #endif 14261 } 14262 #endif 14263 #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) 14264 static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14265 { 14266 #if defined(MA_ARM) 14267 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); 14268 #else 14269 (void)order; 14270 return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); 14271 #endif 14272 } 14273 #else 14274 #endif 14275 #endif 14276 #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) 14277 static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14278 { 14279 ma_uint64 oldValue; 14280 do { 14281 oldValue = *dst; 14282 } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); 14283 (void)order; 14284 return oldValue; 14285 } 14286 #endif 14287 #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) 14288 #if defined(MA_ATOMIC_HAS_8) 14289 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14290 { 14291 ma_uint8 result = 0; 14292 (void)order; 14293 __asm { 14294 mov ecx, dst 14295 mov al, src 14296 lock xadd [ecx], al 14297 mov result, al 14298 } 14299 return result; 14300 } 14301 #endif 14302 #if defined(MA_ATOMIC_HAS_16) 14303 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14304 { 14305 ma_uint16 result = 0; 14306 (void)order; 14307 __asm { 14308 mov ecx, dst 14309 mov ax, src 14310 lock xadd [ecx], ax 14311 mov result, ax 14312 } 14313 return result; 14314 } 14315 #endif 14316 #if defined(MA_ATOMIC_HAS_32) 14317 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14318 { 14319 ma_uint32 result = 0; 14320 (void)order; 14321 __asm { 14322 mov ecx, dst 14323 mov eax, src 14324 lock xadd [ecx], eax 14325 mov result, eax 14326 } 14327 return result; 14328 } 14329 #endif 14330 #else 14331 #if defined(MA_ATOMIC_HAS_8) 14332 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14333 { 14334 #if defined(MA_ARM) 14335 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); 14336 #else 14337 (void)order; 14338 return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); 14339 #endif 14340 } 14341 #endif 14342 #if defined(MA_ATOMIC_HAS_16) 14343 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14344 { 14345 #if defined(MA_ARM) 14346 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); 14347 #else 14348 (void)order; 14349 return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); 14350 #endif 14351 } 14352 #endif 14353 #if defined(MA_ATOMIC_HAS_32) 14354 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14355 { 14356 #if defined(MA_ARM) 14357 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); 14358 #else 14359 (void)order; 14360 return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); 14361 #endif 14362 } 14363 #endif 14364 #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) 14365 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14366 { 14367 #if defined(MA_ARM) 14368 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); 14369 #else 14370 (void)order; 14371 return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); 14372 #endif 14373 } 14374 #else 14375 #endif 14376 #endif 14377 #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) 14378 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14379 { 14380 ma_uint64 oldValue; 14381 ma_uint64 newValue; 14382 do { 14383 oldValue = *dst; 14384 newValue = oldValue + src; 14385 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 14386 (void)order; 14387 return oldValue; 14388 } 14389 #endif 14390 #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) 14391 static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) 14392 { 14393 (void)order; 14394 __asm { 14395 lock add [esp], 0 14396 } 14397 } 14398 #else 14399 #if defined(MA_X64) 14400 #define ma_atomic_thread_fence(order) __faststorefence(), (void)order 14401 #elif defined(MA_ARM64) 14402 #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order 14403 #else 14404 static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) 14405 { 14406 volatile ma_uint32 barrier = 0; 14407 ma_atomic_fetch_add_explicit_32(&barrier, 0, order); 14408 } 14409 #endif 14410 #endif 14411 #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) 14412 #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) 14413 #if defined(MA_ATOMIC_HAS_8) 14414 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) 14415 { 14416 #if defined(MA_ARM) 14417 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); 14418 #else 14419 (void)order; 14420 return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); 14421 #endif 14422 } 14423 #endif 14424 #if defined(MA_ATOMIC_HAS_16) 14425 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) 14426 { 14427 #if defined(MA_ARM) 14428 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); 14429 #else 14430 (void)order; 14431 return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); 14432 #endif 14433 } 14434 #endif 14435 #if defined(MA_ATOMIC_HAS_32) 14436 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) 14437 { 14438 #if defined(MA_ARM) 14439 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); 14440 #else 14441 (void)order; 14442 return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); 14443 #endif 14444 } 14445 #endif 14446 #if defined(MA_ATOMIC_HAS_64) 14447 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) 14448 { 14449 #if defined(MA_ARM) 14450 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); 14451 #else 14452 (void)order; 14453 return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); 14454 #endif 14455 } 14456 #endif 14457 #if defined(MA_ATOMIC_HAS_8) 14458 #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) 14459 #endif 14460 #if defined(MA_ATOMIC_HAS_16) 14461 #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) 14462 #endif 14463 #if defined(MA_ATOMIC_HAS_32) 14464 #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) 14465 #endif 14466 #if defined(MA_ATOMIC_HAS_64) 14467 #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) 14468 #endif 14469 #if defined(MA_ATOMIC_HAS_8) 14470 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14471 { 14472 ma_uint8 oldValue; 14473 ma_uint8 newValue; 14474 do { 14475 oldValue = *dst; 14476 newValue = (ma_uint8)(oldValue - src); 14477 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 14478 (void)order; 14479 return oldValue; 14480 } 14481 #endif 14482 #if defined(MA_ATOMIC_HAS_16) 14483 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14484 { 14485 ma_uint16 oldValue; 14486 ma_uint16 newValue; 14487 do { 14488 oldValue = *dst; 14489 newValue = (ma_uint16)(oldValue - src); 14490 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 14491 (void)order; 14492 return oldValue; 14493 } 14494 #endif 14495 #if defined(MA_ATOMIC_HAS_32) 14496 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14497 { 14498 ma_uint32 oldValue; 14499 ma_uint32 newValue; 14500 do { 14501 oldValue = *dst; 14502 newValue = oldValue - src; 14503 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 14504 (void)order; 14505 return oldValue; 14506 } 14507 #endif 14508 #if defined(MA_ATOMIC_HAS_64) 14509 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14510 { 14511 ma_uint64 oldValue; 14512 ma_uint64 newValue; 14513 do { 14514 oldValue = *dst; 14515 newValue = oldValue - src; 14516 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 14517 (void)order; 14518 return oldValue; 14519 } 14520 #endif 14521 #if defined(MA_ATOMIC_HAS_8) 14522 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14523 { 14524 #if defined(MA_ARM) 14525 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); 14526 #else 14527 ma_uint8 oldValue; 14528 ma_uint8 newValue; 14529 do { 14530 oldValue = *dst; 14531 newValue = (ma_uint8)(oldValue & src); 14532 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 14533 (void)order; 14534 return oldValue; 14535 #endif 14536 } 14537 #endif 14538 #if defined(MA_ATOMIC_HAS_16) 14539 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14540 { 14541 #if defined(MA_ARM) 14542 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); 14543 #else 14544 ma_uint16 oldValue; 14545 ma_uint16 newValue; 14546 do { 14547 oldValue = *dst; 14548 newValue = (ma_uint16)(oldValue & src); 14549 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 14550 (void)order; 14551 return oldValue; 14552 #endif 14553 } 14554 #endif 14555 #if defined(MA_ATOMIC_HAS_32) 14556 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14557 { 14558 #if defined(MA_ARM) 14559 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); 14560 #else 14561 ma_uint32 oldValue; 14562 ma_uint32 newValue; 14563 do { 14564 oldValue = *dst; 14565 newValue = oldValue & src; 14566 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 14567 (void)order; 14568 return oldValue; 14569 #endif 14570 } 14571 #endif 14572 #if defined(MA_ATOMIC_HAS_64) 14573 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14574 { 14575 #if defined(MA_ARM) 14576 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); 14577 #else 14578 ma_uint64 oldValue; 14579 ma_uint64 newValue; 14580 do { 14581 oldValue = *dst; 14582 newValue = oldValue & src; 14583 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 14584 (void)order; 14585 return oldValue; 14586 #endif 14587 } 14588 #endif 14589 #if defined(MA_ATOMIC_HAS_8) 14590 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14591 { 14592 #if defined(MA_ARM) 14593 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); 14594 #else 14595 ma_uint8 oldValue; 14596 ma_uint8 newValue; 14597 do { 14598 oldValue = *dst; 14599 newValue = (ma_uint8)(oldValue ^ src); 14600 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 14601 (void)order; 14602 return oldValue; 14603 #endif 14604 } 14605 #endif 14606 #if defined(MA_ATOMIC_HAS_16) 14607 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14608 { 14609 #if defined(MA_ARM) 14610 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); 14611 #else 14612 ma_uint16 oldValue; 14613 ma_uint16 newValue; 14614 do { 14615 oldValue = *dst; 14616 newValue = (ma_uint16)(oldValue ^ src); 14617 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 14618 (void)order; 14619 return oldValue; 14620 #endif 14621 } 14622 #endif 14623 #if defined(MA_ATOMIC_HAS_32) 14624 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14625 { 14626 #if defined(MA_ARM) 14627 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); 14628 #else 14629 ma_uint32 oldValue; 14630 ma_uint32 newValue; 14631 do { 14632 oldValue = *dst; 14633 newValue = oldValue ^ src; 14634 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 14635 (void)order; 14636 return oldValue; 14637 #endif 14638 } 14639 #endif 14640 #if defined(MA_ATOMIC_HAS_64) 14641 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14642 { 14643 #if defined(MA_ARM) 14644 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); 14645 #else 14646 ma_uint64 oldValue; 14647 ma_uint64 newValue; 14648 do { 14649 oldValue = *dst; 14650 newValue = oldValue ^ src; 14651 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 14652 (void)order; 14653 return oldValue; 14654 #endif 14655 } 14656 #endif 14657 #if defined(MA_ATOMIC_HAS_8) 14658 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14659 { 14660 #if defined(MA_ARM) 14661 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); 14662 #else 14663 ma_uint8 oldValue; 14664 ma_uint8 newValue; 14665 do { 14666 oldValue = *dst; 14667 newValue = (ma_uint8)(oldValue | src); 14668 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 14669 (void)order; 14670 return oldValue; 14671 #endif 14672 } 14673 #endif 14674 #if defined(MA_ATOMIC_HAS_16) 14675 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14676 { 14677 #if defined(MA_ARM) 14678 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); 14679 #else 14680 ma_uint16 oldValue; 14681 ma_uint16 newValue; 14682 do { 14683 oldValue = *dst; 14684 newValue = (ma_uint16)(oldValue | src); 14685 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 14686 (void)order; 14687 return oldValue; 14688 #endif 14689 } 14690 #endif 14691 #if defined(MA_ATOMIC_HAS_32) 14692 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14693 { 14694 #if defined(MA_ARM) 14695 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); 14696 #else 14697 ma_uint32 oldValue; 14698 ma_uint32 newValue; 14699 do { 14700 oldValue = *dst; 14701 newValue = oldValue | src; 14702 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 14703 (void)order; 14704 return oldValue; 14705 #endif 14706 } 14707 #endif 14708 #if defined(MA_ATOMIC_HAS_64) 14709 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14710 { 14711 #if defined(MA_ARM) 14712 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); 14713 #else 14714 ma_uint64 oldValue; 14715 ma_uint64 newValue; 14716 do { 14717 oldValue = *dst; 14718 newValue = oldValue | src; 14719 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 14720 (void)order; 14721 return oldValue; 14722 #endif 14723 } 14724 #endif 14725 #if defined(MA_ATOMIC_HAS_8) 14726 #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) 14727 #endif 14728 #if defined(MA_ATOMIC_HAS_16) 14729 #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) 14730 #endif 14731 #if defined(MA_ATOMIC_HAS_32) 14732 #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) 14733 #endif 14734 #if defined(MA_ATOMIC_HAS_64) 14735 #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) 14736 #endif 14737 #if defined(MA_ATOMIC_HAS_8) 14738 #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) 14739 #endif 14740 #if defined(MA_ATOMIC_HAS_16) 14741 #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) 14742 #endif 14743 #if defined(MA_ATOMIC_HAS_32) 14744 #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) 14745 #endif 14746 #if defined(MA_ATOMIC_HAS_64) 14747 #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) 14748 #endif 14749 #if defined(MA_ATOMIC_HAS_8) 14750 typedef ma_uint8 ma_atomic_flag; 14751 #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) 14752 #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) 14753 #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) 14754 #else 14755 typedef ma_uint32 ma_atomic_flag; 14756 #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) 14757 #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) 14758 #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) 14759 #endif 14760 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) 14761 #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE 14762 #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE 14763 #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED 14764 #define ma_atomic_memory_order_consume __ATOMIC_CONSUME 14765 #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE 14766 #define ma_atomic_memory_order_release __ATOMIC_RELEASE 14767 #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL 14768 #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST 14769 #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") 14770 #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) 14771 #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) 14772 #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) 14773 #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) 14774 #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) 14775 #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) 14776 #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) 14777 #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) 14778 #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) 14779 #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) 14780 #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) 14781 #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) 14782 #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) 14783 #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) 14784 #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) 14785 #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) 14786 #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) 14787 #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) 14788 #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) 14789 #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) 14790 #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) 14791 #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) 14792 #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) 14793 #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) 14794 #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) 14795 #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) 14796 #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) 14797 #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) 14798 #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) 14799 #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) 14800 #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) 14801 #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) 14802 #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) 14803 #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) 14804 #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) 14805 #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) 14806 #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) 14807 #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) 14808 #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) 14809 #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) 14810 #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) 14811 #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) 14812 #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) 14813 #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) 14814 #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) 14815 #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) 14816 #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) 14817 #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) 14818 #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) 14819 #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) 14820 #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) 14821 #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) 14822 #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) 14823 #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) 14824 static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) 14825 { 14826 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); 14827 return expected; 14828 } 14829 static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) 14830 { 14831 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); 14832 return expected; 14833 } 14834 static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) 14835 { 14836 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); 14837 return expected; 14838 } 14839 static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) 14840 { 14841 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); 14842 return expected; 14843 } 14844 typedef ma_uint8 ma_atomic_flag; 14845 #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) 14846 #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) 14847 #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) 14848 #else 14849 #define ma_atomic_memory_order_relaxed 1 14850 #define ma_atomic_memory_order_consume 2 14851 #define ma_atomic_memory_order_acquire 3 14852 #define ma_atomic_memory_order_release 4 14853 #define ma_atomic_memory_order_acq_rel 5 14854 #define ma_atomic_memory_order_seq_cst 6 14855 #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") 14856 #if defined(__GNUC__) 14857 #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order 14858 static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14859 { 14860 if (order > ma_atomic_memory_order_acquire) { 14861 __sync_synchronize(); 14862 } 14863 return __sync_lock_test_and_set(dst, src); 14864 } 14865 static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14866 { 14867 ma_uint16 oldValue; 14868 do { 14869 oldValue = *dst; 14870 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); 14871 (void)order; 14872 return oldValue; 14873 } 14874 static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14875 { 14876 ma_uint32 oldValue; 14877 do { 14878 oldValue = *dst; 14879 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); 14880 (void)order; 14881 return oldValue; 14882 } 14883 static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14884 { 14885 ma_uint64 oldValue; 14886 do { 14887 oldValue = *dst; 14888 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); 14889 (void)order; 14890 return oldValue; 14891 } 14892 static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14893 { 14894 (void)order; 14895 return __sync_fetch_and_add(dst, src); 14896 } 14897 static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14898 { 14899 (void)order; 14900 return __sync_fetch_and_add(dst, src); 14901 } 14902 static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14903 { 14904 (void)order; 14905 return __sync_fetch_and_add(dst, src); 14906 } 14907 static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14908 { 14909 (void)order; 14910 return __sync_fetch_and_add(dst, src); 14911 } 14912 static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14913 { 14914 (void)order; 14915 return __sync_fetch_and_sub(dst, src); 14916 } 14917 static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14918 { 14919 (void)order; 14920 return __sync_fetch_and_sub(dst, src); 14921 } 14922 static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14923 { 14924 (void)order; 14925 return __sync_fetch_and_sub(dst, src); 14926 } 14927 static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14928 { 14929 (void)order; 14930 return __sync_fetch_and_sub(dst, src); 14931 } 14932 static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14933 { 14934 (void)order; 14935 return __sync_fetch_and_or(dst, src); 14936 } 14937 static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14938 { 14939 (void)order; 14940 return __sync_fetch_and_or(dst, src); 14941 } 14942 static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14943 { 14944 (void)order; 14945 return __sync_fetch_and_or(dst, src); 14946 } 14947 static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14948 { 14949 (void)order; 14950 return __sync_fetch_and_or(dst, src); 14951 } 14952 static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14953 { 14954 (void)order; 14955 return __sync_fetch_and_xor(dst, src); 14956 } 14957 static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14958 { 14959 (void)order; 14960 return __sync_fetch_and_xor(dst, src); 14961 } 14962 static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14963 { 14964 (void)order; 14965 return __sync_fetch_and_xor(dst, src); 14966 } 14967 static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14968 { 14969 (void)order; 14970 return __sync_fetch_and_xor(dst, src); 14971 } 14972 static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 14973 { 14974 (void)order; 14975 return __sync_fetch_and_and(dst, src); 14976 } 14977 static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 14978 { 14979 (void)order; 14980 return __sync_fetch_and_and(dst, src); 14981 } 14982 static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 14983 { 14984 (void)order; 14985 return __sync_fetch_and_and(dst, src); 14986 } 14987 static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 14988 { 14989 (void)order; 14990 return __sync_fetch_and_and(dst, src); 14991 } 14992 #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) 14993 #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) 14994 #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) 14995 #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) 14996 #else 14997 #if defined(MA_X86) 14998 #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") 14999 #elif defined(MA_X64) 15000 #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") 15001 #else 15002 #error Unsupported architecture. Please submit a feature request. 15003 #endif 15004 static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) 15005 { 15006 ma_uint8 result; 15007 #if defined(MA_X86) || defined(MA_X64) 15008 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); 15009 #else 15010 #error Unsupported architecture. Please submit a feature request. 15011 #endif 15012 return result; 15013 } 15014 static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) 15015 { 15016 ma_uint16 result; 15017 #if defined(MA_X86) || defined(MA_X64) 15018 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); 15019 #else 15020 #error Unsupported architecture. Please submit a feature request. 15021 #endif 15022 return result; 15023 } 15024 static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) 15025 { 15026 ma_uint32 result; 15027 #if defined(MA_X86) || defined(MA_X64) 15028 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); 15029 #else 15030 #error Unsupported architecture. Please submit a feature request. 15031 #endif 15032 return result; 15033 } 15034 static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) 15035 { 15036 volatile ma_uint64 result; 15037 #if defined(MA_X86) 15038 ma_uint32 resultEAX; 15039 ma_uint32 resultEDX; 15040 __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); 15041 result = ((ma_uint64)resultEDX << 32) | resultEAX; 15042 #elif defined(MA_X64) 15043 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); 15044 #else 15045 #error Unsupported architecture. Please submit a feature request. 15046 #endif 15047 return result; 15048 } 15049 static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15050 { 15051 ma_uint8 result = 0; 15052 (void)order; 15053 #if defined(MA_X86) || defined(MA_X64) 15054 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); 15055 #else 15056 #error Unsupported architecture. Please submit a feature request. 15057 #endif 15058 return result; 15059 } 15060 static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15061 { 15062 ma_uint16 result = 0; 15063 (void)order; 15064 #if defined(MA_X86) || defined(MA_X64) 15065 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); 15066 #else 15067 #error Unsupported architecture. Please submit a feature request. 15068 #endif 15069 return result; 15070 } 15071 static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15072 { 15073 ma_uint32 result; 15074 (void)order; 15075 #if defined(MA_X86) || defined(MA_X64) 15076 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); 15077 #else 15078 #error Unsupported architecture. Please submit a feature request. 15079 #endif 15080 return result; 15081 } 15082 static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15083 { 15084 ma_uint64 result; 15085 (void)order; 15086 #if defined(MA_X86) 15087 do { 15088 result = *dst; 15089 } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); 15090 #elif defined(MA_X64) 15091 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); 15092 #else 15093 #error Unsupported architecture. Please submit a feature request. 15094 #endif 15095 return result; 15096 } 15097 static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15098 { 15099 ma_uint8 result; 15100 (void)order; 15101 #if defined(MA_X86) || defined(MA_X64) 15102 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); 15103 #else 15104 #error Unsupported architecture. Please submit a feature request. 15105 #endif 15106 return result; 15107 } 15108 static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15109 { 15110 ma_uint16 result; 15111 (void)order; 15112 #if defined(MA_X86) || defined(MA_X64) 15113 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); 15114 #else 15115 #error Unsupported architecture. Please submit a feature request. 15116 #endif 15117 return result; 15118 } 15119 static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15120 { 15121 ma_uint32 result; 15122 (void)order; 15123 #if defined(MA_X86) || defined(MA_X64) 15124 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); 15125 #else 15126 #error Unsupported architecture. Please submit a feature request. 15127 #endif 15128 return result; 15129 } 15130 static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15131 { 15132 #if defined(MA_X86) 15133 ma_uint64 oldValue; 15134 ma_uint64 newValue; 15135 (void)order; 15136 do { 15137 oldValue = *dst; 15138 newValue = oldValue + src; 15139 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 15140 return oldValue; 15141 #elif defined(MA_X64) 15142 ma_uint64 result; 15143 (void)order; 15144 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); 15145 return result; 15146 #endif 15147 } 15148 static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15149 { 15150 ma_uint8 oldValue; 15151 ma_uint8 newValue; 15152 do { 15153 oldValue = *dst; 15154 newValue = (ma_uint8)(oldValue - src); 15155 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 15156 (void)order; 15157 return oldValue; 15158 } 15159 static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15160 { 15161 ma_uint16 oldValue; 15162 ma_uint16 newValue; 15163 do { 15164 oldValue = *dst; 15165 newValue = (ma_uint16)(oldValue - src); 15166 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 15167 (void)order; 15168 return oldValue; 15169 } 15170 static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15171 { 15172 ma_uint32 oldValue; 15173 ma_uint32 newValue; 15174 do { 15175 oldValue = *dst; 15176 newValue = oldValue - src; 15177 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 15178 (void)order; 15179 return oldValue; 15180 } 15181 static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15182 { 15183 ma_uint64 oldValue; 15184 ma_uint64 newValue; 15185 do { 15186 oldValue = *dst; 15187 newValue = oldValue - src; 15188 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 15189 (void)order; 15190 return oldValue; 15191 } 15192 static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15193 { 15194 ma_uint8 oldValue; 15195 ma_uint8 newValue; 15196 do { 15197 oldValue = *dst; 15198 newValue = (ma_uint8)(oldValue & src); 15199 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 15200 (void)order; 15201 return oldValue; 15202 } 15203 static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15204 { 15205 ma_uint16 oldValue; 15206 ma_uint16 newValue; 15207 do { 15208 oldValue = *dst; 15209 newValue = (ma_uint16)(oldValue & src); 15210 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 15211 (void)order; 15212 return oldValue; 15213 } 15214 static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15215 { 15216 ma_uint32 oldValue; 15217 ma_uint32 newValue; 15218 do { 15219 oldValue = *dst; 15220 newValue = oldValue & src; 15221 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 15222 (void)order; 15223 return oldValue; 15224 } 15225 static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15226 { 15227 ma_uint64 oldValue; 15228 ma_uint64 newValue; 15229 do { 15230 oldValue = *dst; 15231 newValue = oldValue & src; 15232 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 15233 (void)order; 15234 return oldValue; 15235 } 15236 static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15237 { 15238 ma_uint8 oldValue; 15239 ma_uint8 newValue; 15240 do { 15241 oldValue = *dst; 15242 newValue = (ma_uint8)(oldValue ^ src); 15243 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 15244 (void)order; 15245 return oldValue; 15246 } 15247 static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15248 { 15249 ma_uint16 oldValue; 15250 ma_uint16 newValue; 15251 do { 15252 oldValue = *dst; 15253 newValue = (ma_uint16)(oldValue ^ src); 15254 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 15255 (void)order; 15256 return oldValue; 15257 } 15258 static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15259 { 15260 ma_uint32 oldValue; 15261 ma_uint32 newValue; 15262 do { 15263 oldValue = *dst; 15264 newValue = oldValue ^ src; 15265 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 15266 (void)order; 15267 return oldValue; 15268 } 15269 static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15270 { 15271 ma_uint64 oldValue; 15272 ma_uint64 newValue; 15273 do { 15274 oldValue = *dst; 15275 newValue = oldValue ^ src; 15276 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 15277 (void)order; 15278 return oldValue; 15279 } 15280 static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) 15281 { 15282 ma_uint8 oldValue; 15283 ma_uint8 newValue; 15284 do { 15285 oldValue = *dst; 15286 newValue = (ma_uint8)(oldValue | src); 15287 } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); 15288 (void)order; 15289 return oldValue; 15290 } 15291 static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) 15292 { 15293 ma_uint16 oldValue; 15294 ma_uint16 newValue; 15295 do { 15296 oldValue = *dst; 15297 newValue = (ma_uint16)(oldValue | src); 15298 } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); 15299 (void)order; 15300 return oldValue; 15301 } 15302 static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) 15303 { 15304 ma_uint32 oldValue; 15305 ma_uint32 newValue; 15306 do { 15307 oldValue = *dst; 15308 newValue = oldValue | src; 15309 } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); 15310 (void)order; 15311 return oldValue; 15312 } 15313 static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) 15314 { 15315 ma_uint64 oldValue; 15316 ma_uint64 newValue; 15317 do { 15318 oldValue = *dst; 15319 newValue = oldValue | src; 15320 } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); 15321 (void)order; 15322 return oldValue; 15323 } 15324 #endif 15325 #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) 15326 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) 15327 { 15328 (void)order; 15329 return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); 15330 } 15331 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) 15332 { 15333 (void)order; 15334 return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); 15335 } 15336 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) 15337 { 15338 (void)order; 15339 return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); 15340 } 15341 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) 15342 { 15343 (void)order; 15344 return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); 15345 } 15346 #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) 15347 #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) 15348 #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) 15349 #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) 15350 #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) 15351 #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) 15352 #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) 15353 #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) 15354 #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) 15355 #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) 15356 #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) 15357 #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) 15358 typedef ma_uint8 ma_atomic_flag; 15359 #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) 15360 #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) 15361 #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) 15362 #endif 15363 #if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) 15364 #if defined(MA_ATOMIC_HAS_8) 15365 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15366 { 15367 ma_uint8 expectedValue; 15368 ma_uint8 result; 15369 (void)successOrder; 15370 (void)failureOrder; 15371 expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); 15372 result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); 15373 if (result == expectedValue) { 15374 return 1; 15375 } else { 15376 ma_atomic_store_explicit_8(expected, result, failureOrder); 15377 return 0; 15378 } 15379 } 15380 #endif 15381 #if defined(MA_ATOMIC_HAS_16) 15382 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15383 { 15384 ma_uint16 expectedValue; 15385 ma_uint16 result; 15386 (void)successOrder; 15387 (void)failureOrder; 15388 expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); 15389 result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); 15390 if (result == expectedValue) { 15391 return 1; 15392 } else { 15393 ma_atomic_store_explicit_16(expected, result, failureOrder); 15394 return 0; 15395 } 15396 } 15397 #endif 15398 #if defined(MA_ATOMIC_HAS_32) 15399 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15400 { 15401 ma_uint32 expectedValue; 15402 ma_uint32 result; 15403 (void)successOrder; 15404 (void)failureOrder; 15405 expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); 15406 result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); 15407 if (result == expectedValue) { 15408 return 1; 15409 } else { 15410 ma_atomic_store_explicit_32(expected, result, failureOrder); 15411 return 0; 15412 } 15413 } 15414 #endif 15415 #if defined(MA_ATOMIC_HAS_64) 15416 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15417 { 15418 ma_uint64 expectedValue; 15419 ma_uint64 result; 15420 (void)successOrder; 15421 (void)failureOrder; 15422 expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); 15423 result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); 15424 if (result == expectedValue) { 15425 return 1; 15426 } else { 15427 ma_atomic_store_explicit_64(expected, result, failureOrder); 15428 return 0; 15429 } 15430 } 15431 #endif 15432 #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) 15433 #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) 15434 #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) 15435 #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) 15436 #endif 15437 #if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) 15438 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) 15439 { 15440 (void)ptr; 15441 return 1; 15442 } 15443 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) 15444 { 15445 (void)ptr; 15446 return 1; 15447 } 15448 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) 15449 { 15450 (void)ptr; 15451 return 1; 15452 } 15453 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) 15454 { 15455 (void)ptr; 15456 #if defined(MA_64BIT) 15457 return 1; 15458 #else 15459 #if defined(MA_X86) || defined(MA_X64) 15460 return 1; 15461 #else 15462 return 0; 15463 #endif 15464 #endif 15465 } 15466 #endif 15467 #if defined(MA_64BIT) 15468 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) 15469 { 15470 return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); 15471 } 15472 static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) 15473 { 15474 return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); 15475 } 15476 static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) 15477 { 15478 ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); 15479 } 15480 static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) 15481 { 15482 return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); 15483 } 15484 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15485 { 15486 return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); 15487 } 15488 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15489 { 15490 return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); 15491 } 15492 static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) 15493 { 15494 return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); 15495 } 15496 #elif defined(MA_32BIT) 15497 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) 15498 { 15499 return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); 15500 } 15501 static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) 15502 { 15503 return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); 15504 } 15505 static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) 15506 { 15507 ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); 15508 } 15509 static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) 15510 { 15511 return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); 15512 } 15513 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15514 { 15515 return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); 15516 } 15517 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15518 { 15519 return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); 15520 } 15521 static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) 15522 { 15523 return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); 15524 } 15525 #else 15526 #error Unsupported architecture. 15527 #endif 15528 #define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) 15529 #define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) 15530 #define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) 15531 #define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) 15532 #define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) 15533 #define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15534 #define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15535 #define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) 15536 #define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) 15537 #define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) 15538 #define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) 15539 #define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) 15540 #define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) 15541 #define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) 15542 #define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) 15543 #define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15544 #define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15545 #define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15546 #define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15547 #define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) 15548 #define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) 15549 #define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) 15550 #define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) 15551 #define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15552 #define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15553 #define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15554 #define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15555 #define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15556 #define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15557 #define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15558 #define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15559 #define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15560 #define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15561 #define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15562 #define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15563 #define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15564 #define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15565 #define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15566 #define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15567 #define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15568 #define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15569 #define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15570 #define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15571 #define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15572 #define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15573 #define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15574 #define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15575 #define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) 15576 #define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15577 #define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15578 #define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15579 #define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) 15580 #define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) 15581 #define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) 15582 #define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) 15583 #define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) 15584 #define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) 15585 #define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) 15586 #define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) 15587 #define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) 15588 #define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) 15589 #define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) 15590 #define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) 15591 #define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15592 #define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15593 #define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15594 #define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15595 #define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) 15596 #define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) 15597 #define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) 15598 #define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) 15599 #define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) 15600 #define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15601 #define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15602 #define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15603 #define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) 15604 #define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) 15605 #define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) 15606 #define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) 15607 #define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) 15608 #define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) 15609 #define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) 15610 #define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) 15611 #define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15612 #define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15613 #define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15614 #define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15615 #define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15616 #define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15617 #define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15618 #define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15619 #define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15620 #define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15621 #define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15622 #define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15623 #define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15624 #define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15625 #define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15626 #define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15627 #define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) 15628 #define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) 15629 #define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) 15630 #define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) 15631 #define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) 15632 #define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) 15633 #define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) 15634 #define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) 15635 #define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) 15636 #define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) 15637 #define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) 15638 #define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) 15639 #define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15640 #define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15641 #define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15642 #define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15643 #define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) 15644 #define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) 15645 #define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) 15646 #define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) 15647 #define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15648 #define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15649 #define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15650 #define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15651 #define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15652 #define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15653 #define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15654 #define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15655 #define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15656 #define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15657 #define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15658 #define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15659 #define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15660 #define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15661 #define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15662 #define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15663 #define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15664 #define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15665 #define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15666 #define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15667 #define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15668 #define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15669 #define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15670 #define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15671 #define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15672 #define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15673 #define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15674 #define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15675 #define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) 15676 #define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) 15677 #define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) 15678 #define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) 15679 #define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) 15680 #define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) 15681 #define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) 15682 #define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) 15683 typedef union 15684 { 15685 ma_uint32 i; 15686 float f; 15687 } ma_atomic_if32; 15688 typedef union 15689 { 15690 ma_uint64 i; 15691 double f; 15692 } ma_atomic_if64; 15693 #define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) 15694 #define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) 15695 static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15696 { 15697 ma_atomic_if32 x; 15698 x.f = src; 15699 ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); 15700 } 15701 static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15702 { 15703 ma_atomic_if64 x; 15704 x.f = src; 15705 ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); 15706 } 15707 static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) 15708 { 15709 ma_atomic_if32 r; 15710 r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); 15711 return r.f; 15712 } 15713 static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) 15714 { 15715 ma_atomic_if64 r; 15716 r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); 15717 return r.f; 15718 } 15719 static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15720 { 15721 ma_atomic_if32 r; 15722 ma_atomic_if32 x; 15723 x.f = src; 15724 r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); 15725 return r.f; 15726 } 15727 static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15728 { 15729 ma_atomic_if64 r; 15730 ma_atomic_if64 x; 15731 x.f = src; 15732 r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); 15733 return r.f; 15734 } 15735 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15736 { 15737 ma_atomic_if32 d; 15738 d.f = desired; 15739 return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); 15740 } 15741 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15742 { 15743 ma_atomic_if64 d; 15744 d.f = desired; 15745 return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); 15746 } 15747 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15748 { 15749 ma_atomic_if32 d; 15750 d.f = desired; 15751 return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); 15752 } 15753 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) 15754 { 15755 ma_atomic_if64 d; 15756 d.f = desired; 15757 return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); 15758 } 15759 static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15760 { 15761 ma_atomic_if32 r; 15762 ma_atomic_if32 x; 15763 x.f = src; 15764 r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); 15765 return r.f; 15766 } 15767 static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15768 { 15769 ma_atomic_if64 r; 15770 ma_atomic_if64 x; 15771 x.f = src; 15772 r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); 15773 return r.f; 15774 } 15775 static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15776 { 15777 ma_atomic_if32 r; 15778 ma_atomic_if32 x; 15779 x.f = src; 15780 r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); 15781 return r.f; 15782 } 15783 static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15784 { 15785 ma_atomic_if64 r; 15786 ma_atomic_if64 x; 15787 x.f = src; 15788 r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); 15789 return r.f; 15790 } 15791 static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15792 { 15793 ma_atomic_if32 r; 15794 ma_atomic_if32 x; 15795 x.f = src; 15796 r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); 15797 return r.f; 15798 } 15799 static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15800 { 15801 ma_atomic_if64 r; 15802 ma_atomic_if64 x; 15803 x.f = src; 15804 r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); 15805 return r.f; 15806 } 15807 static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15808 { 15809 ma_atomic_if32 r; 15810 ma_atomic_if32 x; 15811 x.f = src; 15812 r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); 15813 return r.f; 15814 } 15815 static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15816 { 15817 ma_atomic_if64 r; 15818 ma_atomic_if64 x; 15819 x.f = src; 15820 r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); 15821 return r.f; 15822 } 15823 static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) 15824 { 15825 ma_atomic_if32 r; 15826 ma_atomic_if32 x; 15827 x.f = src; 15828 r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); 15829 return r.f; 15830 } 15831 static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) 15832 { 15833 ma_atomic_if64 r; 15834 ma_atomic_if64 x; 15835 x.f = src; 15836 r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); 15837 return r.f; 15838 } 15839 #define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) 15840 #define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) 15841 #define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15842 #define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15843 #define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) 15844 #define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) 15845 #define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15846 #define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15847 #define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15848 #define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15849 #define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15850 #define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) 15851 #define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15852 #define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15853 #define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15854 #define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15855 #define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15856 #define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15857 #define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15858 #define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15859 #define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) 15860 #define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) 15861 static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) 15862 { 15863 ma_atomic_if32 r; 15864 ma_atomic_if32 e, d; 15865 e.f = expected; 15866 d.f = desired; 15867 r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); 15868 return r.f; 15869 } 15870 static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) 15871 { 15872 ma_atomic_if64 r; 15873 ma_atomic_if64 e, d; 15874 e.f = expected; 15875 d.f = desired; 15876 r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); 15877 return r.f; 15878 } 15879 typedef ma_atomic_flag ma_atomic_spinlock; 15880 static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) 15881 { 15882 for (;;) { 15883 if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { 15884 break; 15885 } 15886 while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { 15887 } 15888 } 15889 } 15890 static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) 15891 { 15892 ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); 15893 } 15894 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 15895 #pragma GCC diagnostic pop 15896 #endif 15897 #if defined(__cplusplus) 15898 } 15899 #endif 15900 #endif 15901 /* ma_atomic.h end */ 15902 15903 #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ 15904 static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ 15905 { \ 15906 return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ 15907 } \ 15908 static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ 15909 { \ 15910 ma_atomic_store_##c89TypeExtension(&x->value, value); \ 15911 } \ 15912 static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ 15913 { \ 15914 return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ 15915 } \ 15916 static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ 15917 { \ 15918 return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ 15919 } \ 15920 static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ 15921 { \ 15922 return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ 15923 } \ 15924 static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ 15925 { \ 15926 return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ 15927 } \ 15928 static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ 15929 { \ 15930 return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ 15931 } \ 15932 static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ 15933 { \ 15934 return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ 15935 } \ 15936 static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ 15937 { \ 15938 return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ 15939 } \ 15940 static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ 15941 { \ 15942 return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ 15943 } \ 15944 15945 #define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ 15946 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ 15947 { \ 15948 return ma_atomic_load_ptr((void**)&x->value); \ 15949 } \ 15950 static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ 15951 { \ 15952 ma_atomic_store_ptr((void**)&x->value, (void*)value); \ 15953 } \ 15954 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ 15955 { \ 15956 return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ 15957 } \ 15958 static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ 15959 { \ 15960 return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ 15961 } \ 15962 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ 15963 { \ 15964 return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ 15965 } \ 15966 15967 MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) 15968 MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) 15969 MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) 15970 MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) 15971 MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) 15972 15973 #if !defined(MA_NO_DEVICE_IO) 15974 MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) 15975 #endif 15976 15977 15978 MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) 15979 { 15980 /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ 15981 ma_uint64 outputFrameCount; 15982 ma_uint64 preliminaryInputFrameCountFromFrac; 15983 ma_uint64 preliminaryInputFrameCount; 15984 15985 if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { 15986 return 0; 15987 } 15988 15989 if (sampleRateOut == sampleRateIn) { 15990 return frameCountIn; 15991 } 15992 15993 outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; 15994 15995 preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; 15996 preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; 15997 15998 if (preliminaryInputFrameCount <= frameCountIn) { 15999 outputFrameCount += 1; 16000 } 16001 16002 return outputFrameCount; 16003 } 16004 16005 #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE 16006 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 16007 #endif 16008 16009 16010 16011 #if defined(MA_WIN32) 16012 static ma_result ma_result_from_GetLastError(DWORD error) 16013 { 16014 switch (error) 16015 { 16016 case ERROR_SUCCESS: return MA_SUCCESS; 16017 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; 16018 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; 16019 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; 16020 case ERROR_DISK_FULL: return MA_NO_SPACE; 16021 case ERROR_HANDLE_EOF: return MA_AT_END; 16022 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; 16023 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; 16024 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; 16025 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; 16026 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; 16027 default: break; 16028 } 16029 16030 return MA_ERROR; 16031 } 16032 #endif /* MA_WIN32 */ 16033 16034 16035 /******************************************************************************* 16036 16037 Threading 16038 16039 *******************************************************************************/ 16040 static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) 16041 { 16042 if (pSpinlock == NULL) { 16043 return MA_INVALID_ARGS; 16044 } 16045 16046 for (;;) { 16047 if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { 16048 break; 16049 } 16050 16051 while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { 16052 if (yield) { 16053 ma_yield(); 16054 } 16055 } 16056 } 16057 16058 return MA_SUCCESS; 16059 } 16060 16061 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) 16062 { 16063 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); 16064 } 16065 16066 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) 16067 { 16068 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); 16069 } 16070 16071 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) 16072 { 16073 if (pSpinlock == NULL) { 16074 return MA_INVALID_ARGS; 16075 } 16076 16077 ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); 16078 return MA_SUCCESS; 16079 } 16080 16081 16082 #ifndef MA_NO_THREADING 16083 #if defined(MA_POSIX) 16084 #define MA_THREADCALL 16085 typedef void* ma_thread_result; 16086 #elif defined(MA_WIN32) 16087 #define MA_THREADCALL WINAPI 16088 typedef unsigned long ma_thread_result; 16089 #endif 16090 16091 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); 16092 16093 #ifdef MA_POSIX 16094 static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) 16095 { 16096 int result; 16097 pthread_attr_t* pAttr = NULL; 16098 16099 #if !defined(__EMSCRIPTEN__) 16100 /* Try setting the thread priority. It's not critical if anything fails here. */ 16101 pthread_attr_t attr; 16102 if (pthread_attr_init(&attr) == 0) { 16103 int scheduler = -1; 16104 16105 /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ 16106 pAttr = &attr; 16107 16108 /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ 16109 #if !defined(MA_BEOS) 16110 { 16111 if (priority == ma_thread_priority_idle) { 16112 #ifdef SCHED_IDLE 16113 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { 16114 scheduler = SCHED_IDLE; 16115 } 16116 #endif 16117 } else if (priority == ma_thread_priority_realtime) { 16118 #ifdef SCHED_FIFO 16119 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { 16120 scheduler = SCHED_FIFO; 16121 } 16122 #endif 16123 #ifdef MA_LINUX 16124 } else { 16125 scheduler = sched_getscheduler(0); 16126 #endif 16127 } 16128 } 16129 #endif 16130 16131 if (stackSize > 0) { 16132 pthread_attr_setstacksize(&attr, stackSize); 16133 } 16134 16135 if (scheduler != -1) { 16136 int priorityMin = sched_get_priority_min(scheduler); 16137 int priorityMax = sched_get_priority_max(scheduler); 16138 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ 16139 16140 struct sched_param sched; 16141 if (pthread_attr_getschedparam(&attr, &sched) == 0) { 16142 if (priority == ma_thread_priority_idle) { 16143 sched.sched_priority = priorityMin; 16144 } else if (priority == ma_thread_priority_realtime) { 16145 sched.sched_priority = priorityMax; 16146 } else { 16147 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ 16148 if (sched.sched_priority < priorityMin) { 16149 sched.sched_priority = priorityMin; 16150 } 16151 if (sched.sched_priority > priorityMax) { 16152 sched.sched_priority = priorityMax; 16153 } 16154 } 16155 16156 /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ 16157 pthread_attr_setschedparam(&attr, &sched); 16158 } 16159 } 16160 } 16161 #else 16162 /* It's the emscripten build. We'll have a few unused parameters. */ 16163 (void)priority; 16164 (void)stackSize; 16165 #endif 16166 16167 result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); 16168 16169 /* The thread attributes object is no longer required. */ 16170 if (pAttr != NULL) { 16171 pthread_attr_destroy(pAttr); 16172 } 16173 16174 if (result != 0) { 16175 return ma_result_from_errno(result); 16176 } 16177 16178 return MA_SUCCESS; 16179 } 16180 16181 static void ma_thread_wait__posix(ma_thread* pThread) 16182 { 16183 pthread_join((pthread_t)*pThread, NULL); 16184 } 16185 16186 16187 static ma_result ma_mutex_init__posix(ma_mutex* pMutex) 16188 { 16189 int result; 16190 16191 if (pMutex == NULL) { 16192 return MA_INVALID_ARGS; 16193 } 16194 16195 MA_ZERO_OBJECT(pMutex); 16196 16197 result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); 16198 if (result != 0) { 16199 return ma_result_from_errno(result); 16200 } 16201 16202 return MA_SUCCESS; 16203 } 16204 16205 static void ma_mutex_uninit__posix(ma_mutex* pMutex) 16206 { 16207 pthread_mutex_destroy((pthread_mutex_t*)pMutex); 16208 } 16209 16210 static void ma_mutex_lock__posix(ma_mutex* pMutex) 16211 { 16212 pthread_mutex_lock((pthread_mutex_t*)pMutex); 16213 } 16214 16215 static void ma_mutex_unlock__posix(ma_mutex* pMutex) 16216 { 16217 pthread_mutex_unlock((pthread_mutex_t*)pMutex); 16218 } 16219 16220 16221 static ma_result ma_event_init__posix(ma_event* pEvent) 16222 { 16223 int result; 16224 16225 result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); 16226 if (result != 0) { 16227 return ma_result_from_errno(result); 16228 } 16229 16230 result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); 16231 if (result != 0) { 16232 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); 16233 return ma_result_from_errno(result); 16234 } 16235 16236 pEvent->value = 0; 16237 return MA_SUCCESS; 16238 } 16239 16240 static void ma_event_uninit__posix(ma_event* pEvent) 16241 { 16242 pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); 16243 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); 16244 } 16245 16246 static ma_result ma_event_wait__posix(ma_event* pEvent) 16247 { 16248 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); 16249 { 16250 while (pEvent->value == 0) { 16251 pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); 16252 } 16253 pEvent->value = 0; /* Auto-reset. */ 16254 } 16255 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); 16256 16257 return MA_SUCCESS; 16258 } 16259 16260 static ma_result ma_event_signal__posix(ma_event* pEvent) 16261 { 16262 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); 16263 { 16264 pEvent->value = 1; 16265 pthread_cond_signal((pthread_cond_t*)&pEvent->cond); 16266 } 16267 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); 16268 16269 return MA_SUCCESS; 16270 } 16271 16272 16273 static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) 16274 { 16275 int result; 16276 16277 if (pSemaphore == NULL) { 16278 return MA_INVALID_ARGS; 16279 } 16280 16281 pSemaphore->value = initialValue; 16282 16283 result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); 16284 if (result != 0) { 16285 return ma_result_from_errno(result); /* Failed to create mutex. */ 16286 } 16287 16288 result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); 16289 if (result != 0) { 16290 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); 16291 return ma_result_from_errno(result); /* Failed to create condition variable. */ 16292 } 16293 16294 return MA_SUCCESS; 16295 } 16296 16297 static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) 16298 { 16299 if (pSemaphore == NULL) { 16300 return; 16301 } 16302 16303 pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); 16304 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); 16305 } 16306 16307 static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) 16308 { 16309 if (pSemaphore == NULL) { 16310 return MA_INVALID_ARGS; 16311 } 16312 16313 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); 16314 { 16315 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ 16316 while (pSemaphore->value == 0) { 16317 pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); 16318 } 16319 16320 pSemaphore->value -= 1; 16321 } 16322 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); 16323 16324 return MA_SUCCESS; 16325 } 16326 16327 static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) 16328 { 16329 if (pSemaphore == NULL) { 16330 return MA_INVALID_ARGS; 16331 } 16332 16333 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); 16334 { 16335 pSemaphore->value += 1; 16336 pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); 16337 } 16338 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); 16339 16340 return MA_SUCCESS; 16341 } 16342 #elif defined(MA_WIN32) 16343 static int ma_thread_priority_to_win32(ma_thread_priority priority) 16344 { 16345 switch (priority) { 16346 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; 16347 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; 16348 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; 16349 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; 16350 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; 16351 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; 16352 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; 16353 default: return THREAD_PRIORITY_NORMAL; 16354 } 16355 } 16356 16357 static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) 16358 { 16359 DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ 16360 16361 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); 16362 if (*pThread == NULL) { 16363 return ma_result_from_GetLastError(GetLastError()); 16364 } 16365 16366 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); 16367 16368 return MA_SUCCESS; 16369 } 16370 16371 static void ma_thread_wait__win32(ma_thread* pThread) 16372 { 16373 WaitForSingleObject((HANDLE)*pThread, INFINITE); 16374 CloseHandle((HANDLE)*pThread); 16375 } 16376 16377 16378 static ma_result ma_mutex_init__win32(ma_mutex* pMutex) 16379 { 16380 *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); 16381 if (*pMutex == NULL) { 16382 return ma_result_from_GetLastError(GetLastError()); 16383 } 16384 16385 return MA_SUCCESS; 16386 } 16387 16388 static void ma_mutex_uninit__win32(ma_mutex* pMutex) 16389 { 16390 CloseHandle((HANDLE)*pMutex); 16391 } 16392 16393 static void ma_mutex_lock__win32(ma_mutex* pMutex) 16394 { 16395 WaitForSingleObject((HANDLE)*pMutex, INFINITE); 16396 } 16397 16398 static void ma_mutex_unlock__win32(ma_mutex* pMutex) 16399 { 16400 SetEvent((HANDLE)*pMutex); 16401 } 16402 16403 16404 static ma_result ma_event_init__win32(ma_event* pEvent) 16405 { 16406 *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); 16407 if (*pEvent == NULL) { 16408 return ma_result_from_GetLastError(GetLastError()); 16409 } 16410 16411 return MA_SUCCESS; 16412 } 16413 16414 static void ma_event_uninit__win32(ma_event* pEvent) 16415 { 16416 CloseHandle((HANDLE)*pEvent); 16417 } 16418 16419 static ma_result ma_event_wait__win32(ma_event* pEvent) 16420 { 16421 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); 16422 if (result == WAIT_OBJECT_0) { 16423 return MA_SUCCESS; 16424 } 16425 16426 if (result == WAIT_TIMEOUT) { 16427 return MA_TIMEOUT; 16428 } 16429 16430 return ma_result_from_GetLastError(GetLastError()); 16431 } 16432 16433 static ma_result ma_event_signal__win32(ma_event* pEvent) 16434 { 16435 BOOL result = SetEvent((HANDLE)*pEvent); 16436 if (result == 0) { 16437 return ma_result_from_GetLastError(GetLastError()); 16438 } 16439 16440 return MA_SUCCESS; 16441 } 16442 16443 16444 static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) 16445 { 16446 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); 16447 if (*pSemaphore == NULL) { 16448 return ma_result_from_GetLastError(GetLastError()); 16449 } 16450 16451 return MA_SUCCESS; 16452 } 16453 16454 static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) 16455 { 16456 CloseHandle((HANDLE)*pSemaphore); 16457 } 16458 16459 static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) 16460 { 16461 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); 16462 if (result == WAIT_OBJECT_0) { 16463 return MA_SUCCESS; 16464 } 16465 16466 if (result == WAIT_TIMEOUT) { 16467 return MA_TIMEOUT; 16468 } 16469 16470 return ma_result_from_GetLastError(GetLastError()); 16471 } 16472 16473 static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) 16474 { 16475 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); 16476 if (result == 0) { 16477 return ma_result_from_GetLastError(GetLastError()); 16478 } 16479 16480 return MA_SUCCESS; 16481 } 16482 #endif 16483 16484 typedef struct 16485 { 16486 ma_thread_entry_proc entryProc; 16487 void* pData; 16488 ma_allocation_callbacks allocationCallbacks; 16489 } ma_thread_proxy_data; 16490 16491 static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) 16492 { 16493 ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; 16494 ma_thread_entry_proc entryProc; 16495 void* pEntryProcData; 16496 ma_thread_result result; 16497 16498 #if defined(MA_ON_THREAD_ENTRY) 16499 MA_ON_THREAD_ENTRY 16500 #endif 16501 16502 entryProc = pProxyData->entryProc; 16503 pEntryProcData = pProxyData->pData; 16504 16505 /* Free the proxy data before getting into the real thread entry proc. */ 16506 ma_free(pProxyData, &pProxyData->allocationCallbacks); 16507 16508 result = entryProc(pEntryProcData); 16509 16510 #if defined(MA_ON_THREAD_EXIT) 16511 MA_ON_THREAD_EXIT 16512 #endif 16513 16514 return result; 16515 } 16516 16517 static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) 16518 { 16519 ma_result result; 16520 ma_thread_proxy_data* pProxyData; 16521 16522 if (pThread == NULL || entryProc == NULL) { 16523 return MA_INVALID_ARGS; 16524 } 16525 16526 pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ 16527 if (pProxyData == NULL) { 16528 return MA_OUT_OF_MEMORY; 16529 } 16530 16531 #if defined(MA_THREAD_DEFAULT_STACK_SIZE) 16532 if (stackSize == 0) { 16533 stackSize = MA_THREAD_DEFAULT_STACK_SIZE; 16534 } 16535 #endif 16536 16537 pProxyData->entryProc = entryProc; 16538 pProxyData->pData = pData; 16539 ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); 16540 16541 #if defined(MA_POSIX) 16542 result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); 16543 #elif defined(MA_WIN32) 16544 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); 16545 #endif 16546 16547 if (result != MA_SUCCESS) { 16548 ma_free(pProxyData, pAllocationCallbacks); 16549 return result; 16550 } 16551 16552 return MA_SUCCESS; 16553 } 16554 16555 static void ma_thread_wait(ma_thread* pThread) 16556 { 16557 if (pThread == NULL) { 16558 return; 16559 } 16560 16561 #if defined(MA_POSIX) 16562 ma_thread_wait__posix(pThread); 16563 #elif defined(MA_WIN32) 16564 ma_thread_wait__win32(pThread); 16565 #endif 16566 } 16567 16568 16569 MA_API ma_result ma_mutex_init(ma_mutex* pMutex) 16570 { 16571 if (pMutex == NULL) { 16572 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16573 return MA_INVALID_ARGS; 16574 } 16575 16576 #if defined(MA_POSIX) 16577 return ma_mutex_init__posix(pMutex); 16578 #elif defined(MA_WIN32) 16579 return ma_mutex_init__win32(pMutex); 16580 #endif 16581 } 16582 16583 MA_API void ma_mutex_uninit(ma_mutex* pMutex) 16584 { 16585 if (pMutex == NULL) { 16586 return; 16587 } 16588 16589 #if defined(MA_POSIX) 16590 ma_mutex_uninit__posix(pMutex); 16591 #elif defined(MA_WIN32) 16592 ma_mutex_uninit__win32(pMutex); 16593 #endif 16594 } 16595 16596 MA_API void ma_mutex_lock(ma_mutex* pMutex) 16597 { 16598 if (pMutex == NULL) { 16599 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16600 return; 16601 } 16602 16603 #if defined(MA_POSIX) 16604 ma_mutex_lock__posix(pMutex); 16605 #elif defined(MA_WIN32) 16606 ma_mutex_lock__win32(pMutex); 16607 #endif 16608 } 16609 16610 MA_API void ma_mutex_unlock(ma_mutex* pMutex) 16611 { 16612 if (pMutex == NULL) { 16613 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16614 return; 16615 } 16616 16617 #if defined(MA_POSIX) 16618 ma_mutex_unlock__posix(pMutex); 16619 #elif defined(MA_WIN32) 16620 ma_mutex_unlock__win32(pMutex); 16621 #endif 16622 } 16623 16624 16625 MA_API ma_result ma_event_init(ma_event* pEvent) 16626 { 16627 if (pEvent == NULL) { 16628 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16629 return MA_INVALID_ARGS; 16630 } 16631 16632 #if defined(MA_POSIX) 16633 return ma_event_init__posix(pEvent); 16634 #elif defined(MA_WIN32) 16635 return ma_event_init__win32(pEvent); 16636 #endif 16637 } 16638 16639 #if 0 16640 static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) 16641 { 16642 ma_result result; 16643 ma_event* pEvent; 16644 16645 if (ppEvent == NULL) { 16646 return MA_INVALID_ARGS; 16647 } 16648 16649 *ppEvent = NULL; 16650 16651 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); 16652 if (pEvent == NULL) { 16653 return MA_OUT_OF_MEMORY; 16654 } 16655 16656 result = ma_event_init(pEvent); 16657 if (result != MA_SUCCESS) { 16658 ma_free(pEvent, pAllocationCallbacks); 16659 return result; 16660 } 16661 16662 *ppEvent = pEvent; 16663 return result; 16664 } 16665 #endif 16666 16667 MA_API void ma_event_uninit(ma_event* pEvent) 16668 { 16669 if (pEvent == NULL) { 16670 return; 16671 } 16672 16673 #if defined(MA_POSIX) 16674 ma_event_uninit__posix(pEvent); 16675 #elif defined(MA_WIN32) 16676 ma_event_uninit__win32(pEvent); 16677 #endif 16678 } 16679 16680 #if 0 16681 static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) 16682 { 16683 if (pEvent == NULL) { 16684 return; 16685 } 16686 16687 ma_event_uninit(pEvent); 16688 ma_free(pEvent, pAllocationCallbacks); 16689 } 16690 #endif 16691 16692 MA_API ma_result ma_event_wait(ma_event* pEvent) 16693 { 16694 if (pEvent == NULL) { 16695 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ 16696 return MA_INVALID_ARGS; 16697 } 16698 16699 #if defined(MA_POSIX) 16700 return ma_event_wait__posix(pEvent); 16701 #elif defined(MA_WIN32) 16702 return ma_event_wait__win32(pEvent); 16703 #endif 16704 } 16705 16706 MA_API ma_result ma_event_signal(ma_event* pEvent) 16707 { 16708 if (pEvent == NULL) { 16709 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ 16710 return MA_INVALID_ARGS; 16711 } 16712 16713 #if defined(MA_POSIX) 16714 return ma_event_signal__posix(pEvent); 16715 #elif defined(MA_WIN32) 16716 return ma_event_signal__win32(pEvent); 16717 #endif 16718 } 16719 16720 16721 MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) 16722 { 16723 if (pSemaphore == NULL) { 16724 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16725 return MA_INVALID_ARGS; 16726 } 16727 16728 #if defined(MA_POSIX) 16729 return ma_semaphore_init__posix(initialValue, pSemaphore); 16730 #elif defined(MA_WIN32) 16731 return ma_semaphore_init__win32(initialValue, pSemaphore); 16732 #endif 16733 } 16734 16735 MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) 16736 { 16737 if (pSemaphore == NULL) { 16738 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16739 return; 16740 } 16741 16742 #if defined(MA_POSIX) 16743 ma_semaphore_uninit__posix(pSemaphore); 16744 #elif defined(MA_WIN32) 16745 ma_semaphore_uninit__win32(pSemaphore); 16746 #endif 16747 } 16748 16749 MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) 16750 { 16751 if (pSemaphore == NULL) { 16752 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16753 return MA_INVALID_ARGS; 16754 } 16755 16756 #if defined(MA_POSIX) 16757 return ma_semaphore_wait__posix(pSemaphore); 16758 #elif defined(MA_WIN32) 16759 return ma_semaphore_wait__win32(pSemaphore); 16760 #endif 16761 } 16762 16763 MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) 16764 { 16765 if (pSemaphore == NULL) { 16766 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ 16767 return MA_INVALID_ARGS; 16768 } 16769 16770 #if defined(MA_POSIX) 16771 return ma_semaphore_release__posix(pSemaphore); 16772 #elif defined(MA_WIN32) 16773 return ma_semaphore_release__win32(pSemaphore); 16774 #endif 16775 } 16776 #else 16777 /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ 16778 #ifndef MA_NO_DEVICE_IO 16779 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; 16780 #endif 16781 #endif /* MA_NO_THREADING */ 16782 16783 16784 16785 #define MA_FENCE_COUNTER_MAX 0x7FFFFFFF 16786 16787 MA_API ma_result ma_fence_init(ma_fence* pFence) 16788 { 16789 if (pFence == NULL) { 16790 return MA_INVALID_ARGS; 16791 } 16792 16793 MA_ZERO_OBJECT(pFence); 16794 pFence->counter = 0; 16795 16796 #ifndef MA_NO_THREADING 16797 { 16798 ma_result result; 16799 16800 result = ma_event_init(&pFence->e); 16801 if (result != MA_SUCCESS) { 16802 return result; 16803 } 16804 } 16805 #endif 16806 16807 return MA_SUCCESS; 16808 } 16809 16810 MA_API void ma_fence_uninit(ma_fence* pFence) 16811 { 16812 if (pFence == NULL) { 16813 return; 16814 } 16815 16816 #ifndef MA_NO_THREADING 16817 { 16818 ma_event_uninit(&pFence->e); 16819 } 16820 #endif 16821 16822 MA_ZERO_OBJECT(pFence); 16823 } 16824 16825 MA_API ma_result ma_fence_acquire(ma_fence* pFence) 16826 { 16827 if (pFence == NULL) { 16828 return MA_INVALID_ARGS; 16829 } 16830 16831 for (;;) { 16832 ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); 16833 ma_uint32 newCounter = oldCounter + 1; 16834 16835 /* Make sure we're not about to exceed our maximum value. */ 16836 if (newCounter > MA_FENCE_COUNTER_MAX) { 16837 MA_ASSERT(MA_FALSE); 16838 return MA_OUT_OF_RANGE; 16839 } 16840 16841 if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { 16842 return MA_SUCCESS; 16843 } else { 16844 if (oldCounter == MA_FENCE_COUNTER_MAX) { 16845 MA_ASSERT(MA_FALSE); 16846 return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ 16847 } 16848 } 16849 } 16850 16851 /* Should never get here. */ 16852 /*return MA_SUCCESS;*/ 16853 } 16854 16855 MA_API ma_result ma_fence_release(ma_fence* pFence) 16856 { 16857 if (pFence == NULL) { 16858 return MA_INVALID_ARGS; 16859 } 16860 16861 for (;;) { 16862 ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); 16863 ma_uint32 newCounter = oldCounter - 1; 16864 16865 if (oldCounter == 0) { 16866 MA_ASSERT(MA_FALSE); 16867 return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ 16868 } 16869 16870 if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { 16871 #ifndef MA_NO_THREADING 16872 { 16873 if (newCounter == 0) { 16874 ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ 16875 } 16876 } 16877 #endif 16878 16879 return MA_SUCCESS; 16880 } else { 16881 if (oldCounter == 0) { 16882 MA_ASSERT(MA_FALSE); 16883 return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ 16884 } 16885 } 16886 } 16887 16888 /* Should never get here. */ 16889 /*return MA_SUCCESS;*/ 16890 } 16891 16892 MA_API ma_result ma_fence_wait(ma_fence* pFence) 16893 { 16894 if (pFence == NULL) { 16895 return MA_INVALID_ARGS; 16896 } 16897 16898 for (;;) { 16899 ma_uint32 counter; 16900 16901 counter = ma_atomic_load_32(&pFence->counter); 16902 if (counter == 0) { 16903 /* 16904 Counter has hit zero. By the time we get here some other thread may have acquired the 16905 fence again, but that is where the caller needs to take care with how they se the fence. 16906 */ 16907 return MA_SUCCESS; 16908 } 16909 16910 /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ 16911 #ifndef MA_NO_THREADING 16912 { 16913 ma_result result; 16914 16915 result = ma_event_wait(&pFence->e); 16916 if (result != MA_SUCCESS) { 16917 return result; 16918 } 16919 } 16920 #endif 16921 } 16922 16923 /* Should never get here. */ 16924 /*return MA_INVALID_OPERATION;*/ 16925 } 16926 16927 16928 MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) 16929 { 16930 ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; 16931 16932 if (pNotification == NULL) { 16933 return MA_INVALID_ARGS; 16934 } 16935 16936 if (pNotificationCallbacks->onSignal == NULL) { 16937 return MA_NOT_IMPLEMENTED; 16938 } 16939 16940 pNotificationCallbacks->onSignal(pNotification); 16941 return MA_INVALID_ARGS; 16942 } 16943 16944 16945 static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) 16946 { 16947 ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; 16948 } 16949 16950 MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) 16951 { 16952 if (pNotificationPoll == NULL) { 16953 return MA_INVALID_ARGS; 16954 } 16955 16956 pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; 16957 pNotificationPoll->signalled = MA_FALSE; 16958 16959 return MA_SUCCESS; 16960 } 16961 16962 MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) 16963 { 16964 if (pNotificationPoll == NULL) { 16965 return MA_FALSE; 16966 } 16967 16968 return pNotificationPoll->signalled; 16969 } 16970 16971 16972 static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) 16973 { 16974 ma_async_notification_event_signal((ma_async_notification_event*)pNotification); 16975 } 16976 16977 MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) 16978 { 16979 if (pNotificationEvent == NULL) { 16980 return MA_INVALID_ARGS; 16981 } 16982 16983 pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; 16984 16985 #ifndef MA_NO_THREADING 16986 { 16987 ma_result result; 16988 16989 result = ma_event_init(&pNotificationEvent->e); 16990 if (result != MA_SUCCESS) { 16991 return result; 16992 } 16993 16994 return MA_SUCCESS; 16995 } 16996 #else 16997 { 16998 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ 16999 } 17000 #endif 17001 } 17002 17003 MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) 17004 { 17005 if (pNotificationEvent == NULL) { 17006 return MA_INVALID_ARGS; 17007 } 17008 17009 #ifndef MA_NO_THREADING 17010 { 17011 ma_event_uninit(&pNotificationEvent->e); 17012 return MA_SUCCESS; 17013 } 17014 #else 17015 { 17016 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ 17017 } 17018 #endif 17019 } 17020 17021 MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) 17022 { 17023 if (pNotificationEvent == NULL) { 17024 return MA_INVALID_ARGS; 17025 } 17026 17027 #ifndef MA_NO_THREADING 17028 { 17029 return ma_event_wait(&pNotificationEvent->e); 17030 } 17031 #else 17032 { 17033 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ 17034 } 17035 #endif 17036 } 17037 17038 MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) 17039 { 17040 if (pNotificationEvent == NULL) { 17041 return MA_INVALID_ARGS; 17042 } 17043 17044 #ifndef MA_NO_THREADING 17045 { 17046 return ma_event_signal(&pNotificationEvent->e); 17047 } 17048 #else 17049 { 17050 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ 17051 } 17052 #endif 17053 } 17054 17055 17056 17057 /************************************************************************************************************************************************************ 17058 17059 Job Queue 17060 17061 ************************************************************************************************************************************************************/ 17062 MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) 17063 { 17064 ma_slot_allocator_config config; 17065 17066 MA_ZERO_OBJECT(&config); 17067 config.capacity = capacity; 17068 17069 return config; 17070 } 17071 17072 17073 static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) 17074 { 17075 ma_uint32 cap = slotCapacity / 32; 17076 if ((slotCapacity % 32) != 0) { 17077 cap += 1; 17078 } 17079 17080 return cap; 17081 } 17082 17083 static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) 17084 { 17085 return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); 17086 } 17087 17088 17089 typedef struct 17090 { 17091 size_t sizeInBytes; 17092 size_t groupsOffset; 17093 size_t slotsOffset; 17094 } ma_slot_allocator_heap_layout; 17095 17096 static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) 17097 { 17098 MA_ASSERT(pHeapLayout != NULL); 17099 17100 MA_ZERO_OBJECT(pHeapLayout); 17101 17102 if (pConfig == NULL) { 17103 return MA_INVALID_ARGS; 17104 } 17105 17106 if (pConfig->capacity == 0) { 17107 return MA_INVALID_ARGS; 17108 } 17109 17110 pHeapLayout->sizeInBytes = 0; 17111 17112 /* Groups. */ 17113 pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; 17114 pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); 17115 17116 /* Slots. */ 17117 pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; 17118 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); 17119 17120 return MA_SUCCESS; 17121 } 17122 17123 MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) 17124 { 17125 ma_result result; 17126 ma_slot_allocator_heap_layout layout; 17127 17128 if (pHeapSizeInBytes == NULL) { 17129 return MA_INVALID_ARGS; 17130 } 17131 17132 *pHeapSizeInBytes = 0; 17133 17134 result = ma_slot_allocator_get_heap_layout(pConfig, &layout); 17135 if (result != MA_SUCCESS) { 17136 return result; 17137 } 17138 17139 *pHeapSizeInBytes = layout.sizeInBytes; 17140 17141 return result; 17142 } 17143 17144 MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) 17145 { 17146 ma_result result; 17147 ma_slot_allocator_heap_layout heapLayout; 17148 17149 if (pAllocator == NULL) { 17150 return MA_INVALID_ARGS; 17151 } 17152 17153 MA_ZERO_OBJECT(pAllocator); 17154 17155 if (pHeap == NULL) { 17156 return MA_INVALID_ARGS; 17157 } 17158 17159 result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); 17160 if (result != MA_SUCCESS) { 17161 return result; 17162 } 17163 17164 pAllocator->_pHeap = pHeap; 17165 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 17166 17167 pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); 17168 pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); 17169 pAllocator->capacity = pConfig->capacity; 17170 17171 return MA_SUCCESS; 17172 } 17173 17174 MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) 17175 { 17176 ma_result result; 17177 size_t heapSizeInBytes; 17178 void* pHeap; 17179 17180 result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); 17181 if (result != MA_SUCCESS) { 17182 return result; /* Failed to retrieve the size of the heap allocation. */ 17183 } 17184 17185 if (heapSizeInBytes > 0) { 17186 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 17187 if (pHeap == NULL) { 17188 return MA_OUT_OF_MEMORY; 17189 } 17190 } else { 17191 pHeap = NULL; 17192 } 17193 17194 result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); 17195 if (result != MA_SUCCESS) { 17196 ma_free(pHeap, pAllocationCallbacks); 17197 return result; 17198 } 17199 17200 pAllocator->_ownsHeap = MA_TRUE; 17201 return MA_SUCCESS; 17202 } 17203 17204 MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) 17205 { 17206 if (pAllocator == NULL) { 17207 return; 17208 } 17209 17210 if (pAllocator->_ownsHeap) { 17211 ma_free(pAllocator->_pHeap, pAllocationCallbacks); 17212 } 17213 } 17214 17215 MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) 17216 { 17217 ma_uint32 iAttempt; 17218 const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ 17219 17220 if (pAllocator == NULL || pSlot == NULL) { 17221 return MA_INVALID_ARGS; 17222 } 17223 17224 for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { 17225 /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ 17226 ma_uint32 iGroup; 17227 for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { 17228 /* CAS */ 17229 for (;;) { 17230 ma_uint32 oldBitfield; 17231 ma_uint32 newBitfield; 17232 ma_uint32 bitOffset; 17233 17234 oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ 17235 17236 /* Fast check to see if anything is available. */ 17237 if (oldBitfield == 0xFFFFFFFF) { 17238 break; /* No available bits in this bitfield. */ 17239 } 17240 17241 bitOffset = ma_ffs_32(~oldBitfield); 17242 MA_ASSERT(bitOffset < 32); 17243 17244 newBitfield = oldBitfield | (1 << bitOffset); 17245 17246 if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { 17247 ma_uint32 slotIndex; 17248 17249 /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ 17250 ma_atomic_fetch_add_32(&pAllocator->count, 1); 17251 17252 /* The slot index is required for constructing the output value. */ 17253 slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ 17254 if (slotIndex >= pAllocator->capacity) { 17255 return MA_OUT_OF_MEMORY; 17256 } 17257 17258 /* Increment the reference count before constructing the output value. */ 17259 pAllocator->pSlots[slotIndex] += 1; 17260 17261 /* Construct the output value. */ 17262 *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); 17263 17264 return MA_SUCCESS; 17265 } 17266 } 17267 } 17268 17269 /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ 17270 if (pAllocator->count < pAllocator->capacity) { 17271 ma_yield(); 17272 } else { 17273 return MA_OUT_OF_MEMORY; 17274 } 17275 } 17276 17277 /* We couldn't find a slot within the maximum number of attempts. */ 17278 return MA_OUT_OF_MEMORY; 17279 } 17280 17281 MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) 17282 { 17283 ma_uint32 iGroup; 17284 ma_uint32 iBit; 17285 17286 if (pAllocator == NULL) { 17287 return MA_INVALID_ARGS; 17288 } 17289 17290 iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ 17291 iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ 17292 17293 if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { 17294 return MA_INVALID_ARGS; 17295 } 17296 17297 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ 17298 17299 while (ma_atomic_load_32(&pAllocator->count) > 0) { 17300 /* CAS */ 17301 ma_uint32 oldBitfield; 17302 ma_uint32 newBitfield; 17303 17304 oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ 17305 newBitfield = oldBitfield & ~(1 << iBit); 17306 17307 /* Debugging for checking for double-frees. */ 17308 #if defined(MA_DEBUG_OUTPUT) 17309 { 17310 if ((oldBitfield & (1 << iBit)) == 0) { 17311 MA_ASSERT(MA_FALSE); /* Double free detected.*/ 17312 } 17313 } 17314 #endif 17315 17316 if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { 17317 ma_atomic_fetch_sub_32(&pAllocator->count, 1); 17318 return MA_SUCCESS; 17319 } 17320 } 17321 17322 /* Getting here means there are no allocations available for freeing. */ 17323 return MA_INVALID_OPERATION; 17324 } 17325 17326 17327 #define MA_JOB_ID_NONE ~((ma_uint64)0) 17328 #define MA_JOB_SLOT_NONE (ma_uint16)(~0) 17329 17330 static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) 17331 { 17332 return (ma_uint32)(toc >> 32); 17333 } 17334 17335 static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) 17336 { 17337 return (ma_uint16)(toc & 0x0000FFFF); 17338 } 17339 17340 static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) 17341 { 17342 return (ma_uint16)((toc & 0xFFFF0000) >> 16); 17343 } 17344 17345 static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) 17346 { 17347 return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); 17348 } 17349 17350 static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) 17351 { 17352 /* Clear the reference count first. */ 17353 toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); 17354 toc = toc | ((ma_uint64)refcount << 32); 17355 17356 return toc; 17357 } 17358 17359 17360 MA_API ma_job ma_job_init(ma_uint16 code) 17361 { 17362 ma_job job; 17363 17364 MA_ZERO_OBJECT(&job); 17365 job.toc.breakup.code = code; 17366 job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ 17367 job.next = MA_JOB_ID_NONE; 17368 17369 return job; 17370 } 17371 17372 17373 static ma_result ma_job_process__noop(ma_job* pJob); 17374 static ma_result ma_job_process__quit(ma_job* pJob); 17375 static ma_result ma_job_process__custom(ma_job* pJob); 17376 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); 17377 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); 17378 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); 17379 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); 17380 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); 17381 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); 17382 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); 17383 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); 17384 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); 17385 17386 #if !defined(MA_NO_DEVICE_IO) 17387 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); 17388 #endif 17389 17390 static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = 17391 { 17392 /* Miscellaneous. */ 17393 ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ 17394 ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ 17395 17396 /* Resource Manager. */ 17397 ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ 17398 ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ 17399 ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ 17400 ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ 17401 ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ 17402 ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ 17403 ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ 17404 ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ 17405 ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ 17406 17407 /* Device. */ 17408 #if !defined(MA_NO_DEVICE_IO) 17409 ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ 17410 #endif 17411 }; 17412 17413 MA_API ma_result ma_job_process(ma_job* pJob) 17414 { 17415 if (pJob == NULL) { 17416 return MA_INVALID_ARGS; 17417 } 17418 17419 if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { 17420 return MA_INVALID_OPERATION; 17421 } 17422 17423 return g_jobVTable[pJob->toc.breakup.code](pJob); 17424 } 17425 17426 static ma_result ma_job_process__noop(ma_job* pJob) 17427 { 17428 MA_ASSERT(pJob != NULL); 17429 17430 /* No-op. */ 17431 (void)pJob; 17432 17433 return MA_SUCCESS; 17434 } 17435 17436 static ma_result ma_job_process__quit(ma_job* pJob) 17437 { 17438 return ma_job_process__noop(pJob); 17439 } 17440 17441 static ma_result ma_job_process__custom(ma_job* pJob) 17442 { 17443 MA_ASSERT(pJob != NULL); 17444 17445 /* No-op if there's no callback. */ 17446 if (pJob->data.custom.proc == NULL) { 17447 return MA_SUCCESS; 17448 } 17449 17450 return pJob->data.custom.proc(pJob); 17451 } 17452 17453 17454 17455 MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) 17456 { 17457 ma_job_queue_config config; 17458 17459 config.flags = flags; 17460 config.capacity = capacity; 17461 17462 return config; 17463 } 17464 17465 17466 typedef struct 17467 { 17468 size_t sizeInBytes; 17469 size_t allocatorOffset; 17470 size_t jobsOffset; 17471 } ma_job_queue_heap_layout; 17472 17473 static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) 17474 { 17475 ma_result result; 17476 17477 MA_ASSERT(pHeapLayout != NULL); 17478 17479 MA_ZERO_OBJECT(pHeapLayout); 17480 17481 if (pConfig == NULL) { 17482 return MA_INVALID_ARGS; 17483 } 17484 17485 if (pConfig->capacity == 0) { 17486 return MA_INVALID_ARGS; 17487 } 17488 17489 pHeapLayout->sizeInBytes = 0; 17490 17491 /* Allocator. */ 17492 { 17493 ma_slot_allocator_config allocatorConfig; 17494 size_t allocatorHeapSizeInBytes; 17495 17496 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); 17497 result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); 17498 if (result != MA_SUCCESS) { 17499 return result; 17500 } 17501 17502 pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; 17503 pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; 17504 } 17505 17506 /* Jobs. */ 17507 pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; 17508 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); 17509 17510 return MA_SUCCESS; 17511 } 17512 17513 MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) 17514 { 17515 ma_result result; 17516 ma_job_queue_heap_layout layout; 17517 17518 if (pHeapSizeInBytes == NULL) { 17519 return MA_INVALID_ARGS; 17520 } 17521 17522 *pHeapSizeInBytes = 0; 17523 17524 result = ma_job_queue_get_heap_layout(pConfig, &layout); 17525 if (result != MA_SUCCESS) { 17526 return result; 17527 } 17528 17529 *pHeapSizeInBytes = layout.sizeInBytes; 17530 17531 return MA_SUCCESS; 17532 } 17533 17534 MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) 17535 { 17536 ma_result result; 17537 ma_job_queue_heap_layout heapLayout; 17538 ma_slot_allocator_config allocatorConfig; 17539 17540 if (pQueue == NULL) { 17541 return MA_INVALID_ARGS; 17542 } 17543 17544 MA_ZERO_OBJECT(pQueue); 17545 17546 result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); 17547 if (result != MA_SUCCESS) { 17548 return result; 17549 } 17550 17551 pQueue->_pHeap = pHeap; 17552 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 17553 17554 pQueue->flags = pConfig->flags; 17555 pQueue->capacity = pConfig->capacity; 17556 pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); 17557 17558 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); 17559 result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); 17560 if (result != MA_SUCCESS) { 17561 return result; 17562 } 17563 17564 /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ 17565 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { 17566 #ifndef MA_NO_THREADING 17567 { 17568 ma_semaphore_init(0, &pQueue->sem); 17569 } 17570 #else 17571 { 17572 /* Threading is disabled and we've requested non-blocking mode. */ 17573 return MA_INVALID_OPERATION; 17574 } 17575 #endif 17576 } 17577 17578 /* 17579 Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is 17580 just a dummy item for giving us the first item in the list which is stored in the "next" member. 17581 */ 17582 ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ 17583 pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; 17584 pQueue->tail = pQueue->head; 17585 17586 return MA_SUCCESS; 17587 } 17588 17589 MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) 17590 { 17591 ma_result result; 17592 size_t heapSizeInBytes; 17593 void* pHeap; 17594 17595 result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); 17596 if (result != MA_SUCCESS) { 17597 return result; 17598 } 17599 17600 if (heapSizeInBytes > 0) { 17601 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 17602 if (pHeap == NULL) { 17603 return MA_OUT_OF_MEMORY; 17604 } 17605 } else { 17606 pHeap = NULL; 17607 } 17608 17609 result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); 17610 if (result != MA_SUCCESS) { 17611 ma_free(pHeap, pAllocationCallbacks); 17612 return result; 17613 } 17614 17615 pQueue->_ownsHeap = MA_TRUE; 17616 return MA_SUCCESS; 17617 } 17618 17619 MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) 17620 { 17621 if (pQueue == NULL) { 17622 return; 17623 } 17624 17625 /* All we need to do is uninitialize the semaphore. */ 17626 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { 17627 #ifndef MA_NO_THREADING 17628 { 17629 ma_semaphore_uninit(&pQueue->sem); 17630 } 17631 #else 17632 { 17633 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ 17634 } 17635 #endif 17636 } 17637 17638 ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); 17639 17640 if (pQueue->_ownsHeap) { 17641 ma_free(pQueue->_pHeap, pAllocationCallbacks); 17642 } 17643 } 17644 17645 static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) 17646 { 17647 /* The new counter is taken from the expected value. */ 17648 return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; 17649 } 17650 17651 MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) 17652 { 17653 /* 17654 Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors 17655 */ 17656 ma_result result; 17657 ma_uint64 slot; 17658 ma_uint64 tail; 17659 ma_uint64 next; 17660 17661 if (pQueue == NULL || pJob == NULL) { 17662 return MA_INVALID_ARGS; 17663 } 17664 17665 /* We need a new slot. */ 17666 result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); 17667 if (result != MA_SUCCESS) { 17668 return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ 17669 } 17670 17671 /* At this point we should have a slot to place the job. */ 17672 MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); 17673 17674 /* We need to put the job into memory before we do anything. */ 17675 pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; 17676 pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ 17677 pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ 17678 pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ 17679 17680 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 17681 ma_spinlock_lock(&pQueue->lock); 17682 #endif 17683 { 17684 /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ 17685 for (;;) { 17686 tail = ma_atomic_load_64(&pQueue->tail); 17687 next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); 17688 17689 if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { 17690 if (ma_job_extract_slot(next) == 0xFFFF) { 17691 if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { 17692 break; 17693 } 17694 } else { 17695 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); 17696 } 17697 } 17698 } 17699 ma_job_queue_cas(&pQueue->tail, tail, slot); 17700 } 17701 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 17702 ma_spinlock_unlock(&pQueue->lock); 17703 #endif 17704 17705 17706 /* Signal the semaphore as the last step if we're using synchronous mode. */ 17707 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { 17708 #ifndef MA_NO_THREADING 17709 { 17710 ma_semaphore_release(&pQueue->sem); 17711 } 17712 #else 17713 { 17714 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ 17715 } 17716 #endif 17717 } 17718 17719 return MA_SUCCESS; 17720 } 17721 17722 MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) 17723 { 17724 ma_uint64 head; 17725 ma_uint64 tail; 17726 ma_uint64 next; 17727 17728 if (pQueue == NULL || pJob == NULL) { 17729 return MA_INVALID_ARGS; 17730 } 17731 17732 /* If we're running in synchronous mode we'll need to wait on a semaphore. */ 17733 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { 17734 #ifndef MA_NO_THREADING 17735 { 17736 ma_semaphore_wait(&pQueue->sem); 17737 } 17738 #else 17739 { 17740 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ 17741 } 17742 #endif 17743 } 17744 17745 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 17746 ma_spinlock_lock(&pQueue->lock); 17747 #endif 17748 { 17749 /* 17750 BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below 17751 is stored. One thread can fall through to the freeing of this item while another is still using "head" for the 17752 retrieval of the "next" variable. 17753 17754 The slot allocator might need to make use of some reference counting to ensure it's only truely freed when 17755 there are no more references to the item. This must be fixed before removing these locks. 17756 */ 17757 17758 /* Now we need to remove the root item from the list. */ 17759 for (;;) { 17760 head = ma_atomic_load_64(&pQueue->head); 17761 tail = ma_atomic_load_64(&pQueue->tail); 17762 next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); 17763 17764 if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { 17765 if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { 17766 if (ma_job_extract_slot(next) == 0xFFFF) { 17767 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 17768 ma_spinlock_unlock(&pQueue->lock); 17769 #endif 17770 return MA_NO_DATA_AVAILABLE; 17771 } 17772 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); 17773 } else { 17774 *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; 17775 if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { 17776 break; 17777 } 17778 } 17779 } 17780 } 17781 } 17782 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE 17783 ma_spinlock_unlock(&pQueue->lock); 17784 #endif 17785 17786 ma_slot_allocator_free(&pQueue->allocator, head); 17787 17788 /* 17789 If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We 17790 could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as 17791 possible. 17792 */ 17793 if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { 17794 ma_job_queue_post(pQueue, pJob); 17795 return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ 17796 } 17797 17798 return MA_SUCCESS; 17799 } 17800 17801 17802 17803 /******************************************************************************* 17804 17805 Dynamic Linking 17806 17807 *******************************************************************************/ 17808 #ifdef MA_POSIX 17809 /* No need for dlfcn.h if we're not using runtime linking. */ 17810 #ifndef MA_NO_RUNTIME_LINKING 17811 #include <dlfcn.h> 17812 #endif 17813 #endif 17814 17815 MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) 17816 { 17817 #ifndef MA_NO_RUNTIME_LINKING 17818 ma_handle handle; 17819 17820 ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); 17821 17822 #ifdef MA_WIN32 17823 /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ 17824 #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) 17825 handle = (ma_handle)LoadLibraryA(filename); 17826 #else 17827 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ 17828 WCHAR filenameW[4096]; 17829 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { 17830 handle = NULL; 17831 } else { 17832 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); 17833 } 17834 #endif 17835 #else 17836 handle = (ma_handle)dlopen(filename, RTLD_NOW); 17837 #endif 17838 17839 /* 17840 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority 17841 backend is a deliberate design choice. Instead I'm logging it as an informational message. 17842 */ 17843 if (handle == NULL) { 17844 ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); 17845 } 17846 17847 return handle; 17848 #else 17849 /* Runtime linking is disabled. */ 17850 (void)pLog; 17851 (void)filename; 17852 return NULL; 17853 #endif 17854 } 17855 17856 MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) 17857 { 17858 #ifndef MA_NO_RUNTIME_LINKING 17859 #ifdef MA_WIN32 17860 FreeLibrary((HMODULE)handle); 17861 #else 17862 dlclose((void*)handle); 17863 #endif 17864 17865 (void)pLog; 17866 #else 17867 /* Runtime linking is disabled. */ 17868 (void)pLog; 17869 (void)handle; 17870 #endif 17871 } 17872 17873 MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) 17874 { 17875 #ifndef MA_NO_RUNTIME_LINKING 17876 ma_proc proc; 17877 17878 ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); 17879 17880 #ifdef _WIN32 17881 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); 17882 #else 17883 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 17884 #pragma GCC diagnostic push 17885 #pragma GCC diagnostic ignored "-Wpedantic" 17886 #endif 17887 proc = (ma_proc)dlsym((void*)handle, symbol); 17888 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 17889 #pragma GCC diagnostic pop 17890 #endif 17891 #endif 17892 17893 if (proc == NULL) { 17894 ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); 17895 } 17896 17897 (void)pLog; /* It's possible for pContext to be unused. */ 17898 return proc; 17899 #else 17900 /* Runtime linking is disabled. */ 17901 (void)pLog; 17902 (void)handle; 17903 (void)symbol; 17904 return NULL; 17905 #endif 17906 } 17907 17908 17909 17910 /************************************************************************************************************************************************************ 17911 ************************************************************************************************************************************************************* 17912 17913 DEVICE I/O 17914 ========== 17915 17916 ************************************************************************************************************************************************************* 17917 ************************************************************************************************************************************************************/ 17918 17919 /* Disable run-time linking on certain backends and platforms. */ 17920 #ifndef MA_NO_RUNTIME_LINKING 17921 #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) 17922 #define MA_NO_RUNTIME_LINKING 17923 #endif 17924 #endif 17925 17926 #ifndef MA_NO_DEVICE_IO 17927 17928 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) 17929 #include <mach/mach_time.h> /* For mach_absolute_time() */ 17930 #endif 17931 17932 #ifdef MA_POSIX 17933 #include <sys/types.h> 17934 #include <unistd.h> 17935 17936 /* No need for dlfcn.h if we're not using runtime linking. */ 17937 #ifndef MA_NO_RUNTIME_LINKING 17938 #include <dlfcn.h> 17939 #endif 17940 #endif 17941 17942 17943 17944 MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) 17945 { 17946 if (pDeviceInfo == NULL) { 17947 return; 17948 } 17949 17950 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { 17951 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; 17952 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 17953 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; 17954 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; 17955 pDeviceInfo->nativeDataFormatCount += 1; 17956 } 17957 } 17958 17959 17960 typedef struct 17961 { 17962 ma_backend backend; 17963 const char* pName; 17964 } ma_backend_info; 17965 17966 static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ 17967 { 17968 {ma_backend_wasapi, "WASAPI"}, 17969 {ma_backend_dsound, "DirectSound"}, 17970 {ma_backend_winmm, "WinMM"}, 17971 {ma_backend_coreaudio, "Core Audio"}, 17972 {ma_backend_sndio, "sndio"}, 17973 {ma_backend_audio4, "audio(4)"}, 17974 {ma_backend_oss, "OSS"}, 17975 {ma_backend_pulseaudio, "PulseAudio"}, 17976 {ma_backend_alsa, "ALSA"}, 17977 {ma_backend_jack, "JACK"}, 17978 {ma_backend_aaudio, "AAudio"}, 17979 {ma_backend_opensl, "OpenSL|ES"}, 17980 {ma_backend_webaudio, "Web Audio"}, 17981 {ma_backend_custom, "Custom"}, 17982 {ma_backend_null, "Null"} 17983 }; 17984 17985 MA_API const char* ma_get_backend_name(ma_backend backend) 17986 { 17987 if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { 17988 return "Unknown"; 17989 } 17990 17991 return gBackendInfo[backend].pName; 17992 } 17993 17994 MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) 17995 { 17996 size_t iBackend; 17997 17998 if (pBackendName == NULL) { 17999 return MA_INVALID_ARGS; 18000 } 18001 18002 for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { 18003 if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { 18004 if (pBackend != NULL) { 18005 *pBackend = gBackendInfo[iBackend].backend; 18006 } 18007 18008 return MA_SUCCESS; 18009 } 18010 } 18011 18012 /* Getting here means the backend name is unknown. */ 18013 return MA_INVALID_ARGS; 18014 } 18015 18016 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) 18017 { 18018 /* 18019 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers 18020 about some enums not being handled by the switch statement. 18021 */ 18022 switch (backend) 18023 { 18024 case ma_backend_wasapi: 18025 #if defined(MA_HAS_WASAPI) 18026 return MA_TRUE; 18027 #else 18028 return MA_FALSE; 18029 #endif 18030 case ma_backend_dsound: 18031 #if defined(MA_HAS_DSOUND) 18032 return MA_TRUE; 18033 #else 18034 return MA_FALSE; 18035 #endif 18036 case ma_backend_winmm: 18037 #if defined(MA_HAS_WINMM) 18038 return MA_TRUE; 18039 #else 18040 return MA_FALSE; 18041 #endif 18042 case ma_backend_coreaudio: 18043 #if defined(MA_HAS_COREAUDIO) 18044 return MA_TRUE; 18045 #else 18046 return MA_FALSE; 18047 #endif 18048 case ma_backend_sndio: 18049 #if defined(MA_HAS_SNDIO) 18050 return MA_TRUE; 18051 #else 18052 return MA_FALSE; 18053 #endif 18054 case ma_backend_audio4: 18055 #if defined(MA_HAS_AUDIO4) 18056 return MA_TRUE; 18057 #else 18058 return MA_FALSE; 18059 #endif 18060 case ma_backend_oss: 18061 #if defined(MA_HAS_OSS) 18062 return MA_TRUE; 18063 #else 18064 return MA_FALSE; 18065 #endif 18066 case ma_backend_pulseaudio: 18067 #if defined(MA_HAS_PULSEAUDIO) 18068 return MA_TRUE; 18069 #else 18070 return MA_FALSE; 18071 #endif 18072 case ma_backend_alsa: 18073 #if defined(MA_HAS_ALSA) 18074 return MA_TRUE; 18075 #else 18076 return MA_FALSE; 18077 #endif 18078 case ma_backend_jack: 18079 #if defined(MA_HAS_JACK) 18080 return MA_TRUE; 18081 #else 18082 return MA_FALSE; 18083 #endif 18084 case ma_backend_aaudio: 18085 #if defined(MA_HAS_AAUDIO) 18086 #if defined(MA_ANDROID) 18087 { 18088 return ma_android_sdk_version() >= 26; 18089 } 18090 #else 18091 return MA_FALSE; 18092 #endif 18093 #else 18094 return MA_FALSE; 18095 #endif 18096 case ma_backend_opensl: 18097 #if defined(MA_HAS_OPENSL) 18098 #if defined(MA_ANDROID) 18099 { 18100 return ma_android_sdk_version() >= 9; 18101 } 18102 #else 18103 return MA_TRUE; 18104 #endif 18105 #else 18106 return MA_FALSE; 18107 #endif 18108 case ma_backend_webaudio: 18109 #if defined(MA_HAS_WEBAUDIO) 18110 return MA_TRUE; 18111 #else 18112 return MA_FALSE; 18113 #endif 18114 case ma_backend_custom: 18115 #if defined(MA_HAS_CUSTOM) 18116 return MA_TRUE; 18117 #else 18118 return MA_FALSE; 18119 #endif 18120 case ma_backend_null: 18121 #if defined(MA_HAS_NULL) 18122 return MA_TRUE; 18123 #else 18124 return MA_FALSE; 18125 #endif 18126 18127 default: return MA_FALSE; 18128 } 18129 } 18130 18131 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) 18132 { 18133 size_t backendCount; 18134 size_t iBackend; 18135 ma_result result = MA_SUCCESS; 18136 18137 if (pBackendCount == NULL) { 18138 return MA_INVALID_ARGS; 18139 } 18140 18141 backendCount = 0; 18142 18143 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { 18144 ma_backend backend = (ma_backend)iBackend; 18145 18146 if (ma_is_backend_enabled(backend)) { 18147 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ 18148 if (backendCount == backendCap) { 18149 result = MA_NO_SPACE; 18150 break; 18151 } else { 18152 pBackends[backendCount] = backend; 18153 backendCount += 1; 18154 } 18155 } 18156 } 18157 18158 if (pBackendCount != NULL) { 18159 *pBackendCount = backendCount; 18160 } 18161 18162 return result; 18163 } 18164 18165 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) 18166 { 18167 switch (backend) 18168 { 18169 case ma_backend_wasapi: return MA_TRUE; 18170 case ma_backend_dsound: return MA_FALSE; 18171 case ma_backend_winmm: return MA_FALSE; 18172 case ma_backend_coreaudio: return MA_FALSE; 18173 case ma_backend_sndio: return MA_FALSE; 18174 case ma_backend_audio4: return MA_FALSE; 18175 case ma_backend_oss: return MA_FALSE; 18176 case ma_backend_pulseaudio: return MA_FALSE; 18177 case ma_backend_alsa: return MA_FALSE; 18178 case ma_backend_jack: return MA_FALSE; 18179 case ma_backend_aaudio: return MA_FALSE; 18180 case ma_backend_opensl: return MA_FALSE; 18181 case ma_backend_webaudio: return MA_FALSE; 18182 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ 18183 case ma_backend_null: return MA_FALSE; 18184 default: return MA_FALSE; 18185 } 18186 } 18187 18188 18189 18190 #if defined(MA_WIN32) 18191 /* WASAPI error codes. */ 18192 #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) 18193 #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) 18194 #define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) 18195 #define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) 18196 #define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) 18197 #define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) 18198 #define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) 18199 #define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) 18200 #define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) 18201 #define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) 18202 #define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) 18203 #define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) 18204 #define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) 18205 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) 18206 #define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) 18207 #define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) 18208 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) 18209 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) 18210 #define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) 18211 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) 18212 #define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) 18213 #define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) 18214 #define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) 18215 #define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) 18216 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) 18217 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) 18218 #define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) 18219 #define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) 18220 #define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) 18221 #define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) 18222 #define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) 18223 #define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) 18224 #define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) 18225 #define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) 18226 #define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) 18227 #define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) 18228 #define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) 18229 #define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) 18230 #define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) 18231 #define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) 18232 18233 #define MA_DS_OK ((HRESULT)0) 18234 #define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) 18235 #define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) 18236 #define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) 18237 #define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ 18238 #define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) 18239 #define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ 18240 #define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) 18241 #define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ 18242 #define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) 18243 #define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ 18244 #define MA_DSERR_NODRIVER ((HRESULT)0x88780078) 18245 #define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) 18246 #define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ 18247 #define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) 18248 #define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) 18249 #define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) 18250 #define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ 18251 #define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ 18252 #define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) 18253 #define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) 18254 #define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) 18255 #define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) 18256 #define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) 18257 #define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) 18258 18259 static ma_result ma_result_from_HRESULT(HRESULT hr) 18260 { 18261 switch (hr) 18262 { 18263 case NOERROR: return MA_SUCCESS; 18264 /*case S_OK: return MA_SUCCESS;*/ 18265 18266 case E_POINTER: return MA_INVALID_ARGS; 18267 case E_UNEXPECTED: return MA_ERROR; 18268 case E_NOTIMPL: return MA_NOT_IMPLEMENTED; 18269 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; 18270 case E_INVALIDARG: return MA_INVALID_ARGS; 18271 case E_NOINTERFACE: return MA_API_NOT_FOUND; 18272 case E_HANDLE: return MA_INVALID_ARGS; 18273 case E_ABORT: return MA_ERROR; 18274 case E_FAIL: return MA_ERROR; 18275 case E_ACCESSDENIED: return MA_ACCESS_DENIED; 18276 18277 /* WASAPI */ 18278 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; 18279 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; 18280 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; 18281 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; 18282 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; 18283 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; 18284 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; 18285 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; 18286 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; 18287 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; 18288 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; 18289 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; 18290 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; 18291 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; 18292 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 18293 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; 18294 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; 18295 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; 18296 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; 18297 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; 18298 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; 18299 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; 18300 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; 18301 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; 18302 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; 18303 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; 18304 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; 18305 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; 18306 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; 18307 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; 18308 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; 18309 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; 18310 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; 18311 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; 18312 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; 18313 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; 18314 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; 18315 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; 18316 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; 18317 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; 18318 18319 /* DirectSound */ 18320 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ 18321 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; 18322 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; 18323 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; 18324 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ 18325 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; 18326 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ 18327 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; 18328 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ 18329 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; 18330 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ 18331 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; 18332 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; 18333 case MA_DSERR_NOAGGREGATION: return MA_ERROR; 18334 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; 18335 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; 18336 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; 18337 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ 18338 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ 18339 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; 18340 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; 18341 case MA_DSERR_SENDLOOP: return MA_DEADLOCK; 18342 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; 18343 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; 18344 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; 18345 18346 default: return MA_ERROR; 18347 } 18348 } 18349 18350 /* PROPVARIANT */ 18351 #define MA_VT_LPWSTR 31 18352 #define MA_VT_BLOB 65 18353 18354 #if defined(_MSC_VER) && !defined(__clang__) 18355 #pragma warning(push) 18356 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ 18357 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 18358 #pragma GCC diagnostic push 18359 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ 18360 #if defined(__clang__) 18361 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ 18362 #endif 18363 #endif 18364 typedef struct 18365 { 18366 WORD vt; 18367 WORD wReserved1; 18368 WORD wReserved2; 18369 WORD wReserved3; 18370 union 18371 { 18372 struct 18373 { 18374 ULONG cbSize; 18375 BYTE* pBlobData; 18376 } blob; 18377 WCHAR* pwszVal; 18378 char pad[16]; /* Just to ensure the size of the struct matches the official version. */ 18379 }; 18380 } MA_PROPVARIANT; 18381 #if defined(_MSC_VER) && !defined(__clang__) 18382 #pragma warning(pop) 18383 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 18384 #pragma GCC diagnostic pop 18385 #endif 18386 18387 typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); 18388 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); 18389 typedef void (WINAPI * MA_PFN_CoUninitialize)(void); 18390 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); 18391 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); 18392 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); 18393 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); 18394 18395 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); 18396 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); 18397 18398 #if defined(MA_WIN32_DESKTOP) 18399 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ 18400 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); 18401 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); 18402 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); 18403 #endif /* MA_WIN32_DESKTOP */ 18404 18405 18406 MA_API size_t ma_strlen_WCHAR(const WCHAR* str) 18407 { 18408 size_t len = 0; 18409 while (str[len] != '\0') { 18410 len += 1; 18411 } 18412 18413 return len; 18414 } 18415 18416 MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) 18417 { 18418 while (*s1 != '\0' && *s1 == *s2) { 18419 s1 += 1; 18420 s2 += 1; 18421 } 18422 18423 return *s1 - *s2; 18424 } 18425 18426 MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) 18427 { 18428 size_t i; 18429 18430 if (dst == 0) { 18431 return 22; 18432 } 18433 if (dstCap == 0) { 18434 return 34; 18435 } 18436 if (src == 0) { 18437 dst[0] = '\0'; 18438 return 22; 18439 } 18440 18441 for (i = 0; i < dstCap && src[i] != '\0'; ++i) { 18442 dst[i] = src[i]; 18443 } 18444 18445 if (i < dstCap) { 18446 dst[i] = '\0'; 18447 return 0; 18448 } 18449 18450 dst[0] = '\0'; 18451 return 34; 18452 } 18453 #endif /* MA_WIN32 */ 18454 18455 18456 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" 18457 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" 18458 18459 18460 18461 18462 /******************************************************************************* 18463 18464 Timing 18465 18466 *******************************************************************************/ 18467 #if defined(MA_WIN32) && !defined(MA_POSIX) 18468 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ 18469 static void ma_timer_init(ma_timer* pTimer) 18470 { 18471 LARGE_INTEGER counter; 18472 18473 if (g_ma_TimerFrequency.QuadPart == 0) { 18474 QueryPerformanceFrequency(&g_ma_TimerFrequency); 18475 } 18476 18477 QueryPerformanceCounter(&counter); 18478 pTimer->counter = counter.QuadPart; 18479 } 18480 18481 static double ma_timer_get_time_in_seconds(ma_timer* pTimer) 18482 { 18483 LARGE_INTEGER counter; 18484 if (!QueryPerformanceCounter(&counter)) { 18485 return 0; 18486 } 18487 18488 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; 18489 } 18490 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) 18491 static ma_uint64 g_ma_TimerFrequency = 0; 18492 static void ma_timer_init(ma_timer* pTimer) 18493 { 18494 mach_timebase_info_data_t baseTime; 18495 mach_timebase_info(&baseTime); 18496 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; 18497 18498 pTimer->counter = mach_absolute_time(); 18499 } 18500 18501 static double ma_timer_get_time_in_seconds(ma_timer* pTimer) 18502 { 18503 ma_uint64 newTimeCounter = mach_absolute_time(); 18504 ma_uint64 oldTimeCounter = pTimer->counter; 18505 18506 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; 18507 } 18508 #elif defined(MA_EMSCRIPTEN) 18509 static MA_INLINE void ma_timer_init(ma_timer* pTimer) 18510 { 18511 pTimer->counterD = emscripten_get_now(); 18512 } 18513 18514 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) 18515 { 18516 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ 18517 } 18518 #else 18519 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L 18520 #if defined(CLOCK_MONOTONIC) 18521 #define MA_CLOCK_ID CLOCK_MONOTONIC 18522 #else 18523 #define MA_CLOCK_ID CLOCK_REALTIME 18524 #endif 18525 18526 static void ma_timer_init(ma_timer* pTimer) 18527 { 18528 struct timespec newTime; 18529 clock_gettime(MA_CLOCK_ID, &newTime); 18530 18531 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; 18532 } 18533 18534 static double ma_timer_get_time_in_seconds(ma_timer* pTimer) 18535 { 18536 ma_uint64 newTimeCounter; 18537 ma_uint64 oldTimeCounter; 18538 18539 struct timespec newTime; 18540 clock_gettime(MA_CLOCK_ID, &newTime); 18541 18542 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; 18543 oldTimeCounter = pTimer->counter; 18544 18545 return (newTimeCounter - oldTimeCounter) / 1000000000.0; 18546 } 18547 #else 18548 static void ma_timer_init(ma_timer* pTimer) 18549 { 18550 struct timeval newTime; 18551 gettimeofday(&newTime, NULL); 18552 18553 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; 18554 } 18555 18556 static double ma_timer_get_time_in_seconds(ma_timer* pTimer) 18557 { 18558 ma_uint64 newTimeCounter; 18559 ma_uint64 oldTimeCounter; 18560 18561 struct timeval newTime; 18562 gettimeofday(&newTime, NULL); 18563 18564 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; 18565 oldTimeCounter = pTimer->counter; 18566 18567 return (newTimeCounter - oldTimeCounter) / 1000000.0; 18568 } 18569 #endif 18570 #endif 18571 18572 18573 18574 #if 0 18575 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) 18576 { 18577 ma_uint32 closestRate = 0; 18578 ma_uint32 closestDiff = 0xFFFFFFFF; 18579 size_t iStandardRate; 18580 18581 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { 18582 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; 18583 ma_uint32 diff; 18584 18585 if (sampleRateIn > standardRate) { 18586 diff = sampleRateIn - standardRate; 18587 } else { 18588 diff = standardRate - sampleRateIn; 18589 } 18590 18591 if (diff == 0) { 18592 return standardRate; /* The input sample rate is a standard rate. */ 18593 } 18594 18595 if (closestDiff > diff) { 18596 closestDiff = diff; 18597 closestRate = standardRate; 18598 } 18599 } 18600 18601 return closestRate; 18602 } 18603 #endif 18604 18605 18606 static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) 18607 { 18608 MA_ASSERT(pDevice != NULL); 18609 18610 if (!pDevice->noDisableDenormals) { 18611 return ma_disable_denormals(); 18612 } else { 18613 return 0; 18614 } 18615 } 18616 18617 static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) 18618 { 18619 MA_ASSERT(pDevice != NULL); 18620 18621 if (!pDevice->noDisableDenormals) { 18622 ma_restore_denormals(prevState); 18623 } else { 18624 /* Do nothing. */ 18625 (void)prevState; 18626 } 18627 } 18628 18629 static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) 18630 { 18631 ma_device_notification notification; 18632 18633 MA_ZERO_OBJECT(¬ification); 18634 notification.pDevice = pDevice; 18635 notification.type = type; 18636 18637 return notification; 18638 } 18639 18640 static void ma_device__on_notification(ma_device_notification notification) 18641 { 18642 MA_ASSERT(notification.pDevice != NULL); 18643 18644 if (notification.pDevice->onNotification != NULL) { 18645 notification.pDevice->onNotification(¬ification); 18646 } 18647 18648 /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ 18649 if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { 18650 notification.pDevice->onStop(notification.pDevice); 18651 } 18652 } 18653 18654 static void ma_device__on_notification_started(ma_device* pDevice) 18655 { 18656 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); 18657 } 18658 18659 static void ma_device__on_notification_stopped(ma_device* pDevice) 18660 { 18661 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); 18662 } 18663 18664 /* Not all platforms support reroute notifications. */ 18665 #if !defined(MA_EMSCRIPTEN) 18666 static void ma_device__on_notification_rerouted(ma_device* pDevice) 18667 { 18668 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); 18669 } 18670 #endif 18671 18672 #if defined(MA_EMSCRIPTEN) 18673 EMSCRIPTEN_KEEPALIVE 18674 void ma_device__on_notification_unlocked(ma_device* pDevice) 18675 { 18676 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); 18677 } 18678 #endif 18679 18680 18681 static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) 18682 { 18683 MA_ASSERT(pDevice != NULL); 18684 MA_ASSERT(pDevice->onData != NULL); 18685 18686 if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { 18687 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); 18688 } 18689 18690 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); 18691 } 18692 18693 static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) 18694 { 18695 MA_ASSERT(pDevice != NULL); 18696 18697 /* Don't read more data from the client if we're in the process of stopping. */ 18698 if (ma_device_get_state(pDevice) == ma_device_state_stopping) { 18699 return; 18700 } 18701 18702 if (pDevice->noFixedSizedCallback) { 18703 /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ 18704 ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); 18705 } else { 18706 /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ 18707 ma_uint32 totalFramesProcessed = 0; 18708 18709 while (totalFramesProcessed < frameCount) { 18710 ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; 18711 ma_uint32 framesToProcessThisIteration = 0; 18712 18713 if (pFramesIn != NULL) { 18714 /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ 18715 if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { 18716 /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ 18717 framesToProcessThisIteration = totalFramesRemaining; 18718 if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { 18719 framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; 18720 } 18721 18722 ma_copy_pcm_frames( 18723 ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), 18724 ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), 18725 framesToProcessThisIteration, 18726 pDevice->capture.format, pDevice->capture.channels); 18727 18728 pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; 18729 } 18730 18731 if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { 18732 /* No room left in the intermediary buffer. Fire the data callback. */ 18733 if (pDevice->type == ma_device_type_duplex) { 18734 /* We'll do the duplex data callback later after we've processed the playback data. */ 18735 } else { 18736 ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); 18737 18738 /* The intermediary buffer has just been drained. */ 18739 pDevice->capture.intermediaryBufferLen = 0; 18740 } 18741 } 18742 } 18743 18744 if (pFramesOut != NULL) { 18745 /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ 18746 if (pDevice->playback.intermediaryBufferLen > 0) { 18747 /* There's some content in the intermediary buffer. Read from that without firing the callback. */ 18748 if (pDevice->type == ma_device_type_duplex) { 18749 /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ 18750 } else { 18751 framesToProcessThisIteration = totalFramesRemaining; 18752 if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { 18753 framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; 18754 } 18755 } 18756 18757 ma_copy_pcm_frames( 18758 ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), 18759 ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), 18760 framesToProcessThisIteration, 18761 pDevice->playback.format, pDevice->playback.channels); 18762 18763 pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; 18764 } 18765 18766 if (pDevice->playback.intermediaryBufferLen == 0) { 18767 /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ 18768 if (pDevice->type == ma_device_type_duplex) { 18769 /* In duplex mode, the data callback will be fired later. Nothing to do here. */ 18770 } else { 18771 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); 18772 18773 /* The intermediary buffer has just been filled. */ 18774 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; 18775 } 18776 } 18777 } 18778 18779 /* If we're in duplex mode we might need to do a refill of the data. */ 18780 if (pDevice->type == ma_device_type_duplex) { 18781 if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { 18782 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); 18783 18784 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ 18785 pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ 18786 } 18787 } 18788 18789 /* Make sure this is only incremented once in the duplex case. */ 18790 totalFramesProcessed += framesToProcessThisIteration; 18791 } 18792 } 18793 } 18794 18795 static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) 18796 { 18797 float masterVolumeFactor; 18798 18799 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ 18800 18801 if (pDevice->onData) { 18802 unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); 18803 { 18804 /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ 18805 if (pFramesIn != NULL && masterVolumeFactor < 1) { 18806 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 18807 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 18808 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 18809 ma_uint32 totalFramesProcessed = 0; 18810 while (totalFramesProcessed < frameCount) { 18811 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; 18812 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { 18813 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; 18814 } 18815 18816 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); 18817 18818 ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); 18819 18820 totalFramesProcessed += framesToProcessThisIteration; 18821 } 18822 } else { 18823 ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); 18824 } 18825 18826 /* Volume control and clipping for playback devices. */ 18827 if (pFramesOut != NULL) { 18828 if (masterVolumeFactor < 1) { 18829 if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ 18830 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); 18831 } 18832 } 18833 18834 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { 18835 ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ 18836 } 18837 } 18838 } 18839 ma_device_restore_denormals(pDevice, prevDenormalState); 18840 } 18841 } 18842 18843 18844 18845 /* A helper function for reading sample data from the client. */ 18846 static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) 18847 { 18848 MA_ASSERT(pDevice != NULL); 18849 MA_ASSERT(frameCount > 0); 18850 MA_ASSERT(pFramesOut != NULL); 18851 18852 if (pDevice->playback.converter.isPassthrough) { 18853 ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); 18854 } else { 18855 ma_result result; 18856 ma_uint64 totalFramesReadOut; 18857 void* pRunningFramesOut; 18858 18859 totalFramesReadOut = 0; 18860 pRunningFramesOut = pFramesOut; 18861 18862 /* 18863 We run slightly different logic depending on whether or not we're using a heap-allocated 18864 buffer for caching input data. This will be the case if the data converter does not have 18865 the ability to retrieve the required input frame count for a given output frame count. 18866 */ 18867 if (pDevice->playback.pInputCache != NULL) { 18868 while (totalFramesReadOut < frameCount) { 18869 ma_uint64 framesToReadThisIterationIn; 18870 ma_uint64 framesToReadThisIterationOut; 18871 18872 /* If there's any data available in the cache, that needs to get processed first. */ 18873 if (pDevice->playback.inputCacheRemaining > 0) { 18874 framesToReadThisIterationOut = (frameCount - totalFramesReadOut); 18875 framesToReadThisIterationIn = framesToReadThisIterationOut; 18876 if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { 18877 framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; 18878 } 18879 18880 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); 18881 if (result != MA_SUCCESS) { 18882 break; 18883 } 18884 18885 pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; 18886 pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; 18887 18888 totalFramesReadOut += framesToReadThisIterationOut; 18889 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 18890 18891 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { 18892 break; /* We're done. */ 18893 } 18894 } 18895 18896 /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ 18897 if (pDevice->playback.inputCacheRemaining == 0) { 18898 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); 18899 18900 pDevice->playback.inputCacheConsumed = 0; 18901 pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; 18902 } 18903 } 18904 } else { 18905 while (totalFramesReadOut < frameCount) { 18906 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ 18907 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 18908 ma_uint64 framesToReadThisIterationIn; 18909 ma_uint64 framesReadThisIterationIn; 18910 ma_uint64 framesToReadThisIterationOut; 18911 ma_uint64 framesReadThisIterationOut; 18912 ma_uint64 requiredInputFrameCount; 18913 18914 framesToReadThisIterationOut = (frameCount - totalFramesReadOut); 18915 framesToReadThisIterationIn = framesToReadThisIterationOut; 18916 if (framesToReadThisIterationIn > intermediaryBufferCap) { 18917 framesToReadThisIterationIn = intermediaryBufferCap; 18918 } 18919 18920 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); 18921 if (framesToReadThisIterationIn > requiredInputFrameCount) { 18922 framesToReadThisIterationIn = requiredInputFrameCount; 18923 } 18924 18925 if (framesToReadThisIterationIn > 0) { 18926 ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); 18927 } 18928 18929 /* 18930 At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any 18931 input frames, we still want to try processing frames because there may some output frames generated from cached input data. 18932 */ 18933 framesReadThisIterationIn = framesToReadThisIterationIn; 18934 framesReadThisIterationOut = framesToReadThisIterationOut; 18935 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); 18936 if (result != MA_SUCCESS) { 18937 break; 18938 } 18939 18940 totalFramesReadOut += framesReadThisIterationOut; 18941 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 18942 18943 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { 18944 break; /* We're done. */ 18945 } 18946 } 18947 } 18948 } 18949 } 18950 18951 /* A helper for sending sample data to the client. */ 18952 static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) 18953 { 18954 MA_ASSERT(pDevice != NULL); 18955 MA_ASSERT(frameCountInDeviceFormat > 0); 18956 MA_ASSERT(pFramesInDeviceFormat != NULL); 18957 18958 if (pDevice->capture.converter.isPassthrough) { 18959 ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); 18960 } else { 18961 ma_result result; 18962 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 18963 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 18964 ma_uint64 totalDeviceFramesProcessed = 0; 18965 ma_uint64 totalClientFramesProcessed = 0; 18966 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; 18967 18968 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ 18969 for (;;) { 18970 ma_uint64 deviceFramesProcessedThisIteration; 18971 ma_uint64 clientFramesProcessedThisIteration; 18972 18973 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); 18974 clientFramesProcessedThisIteration = framesInClientFormatCap; 18975 18976 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); 18977 if (result != MA_SUCCESS) { 18978 break; 18979 } 18980 18981 if (clientFramesProcessedThisIteration > 0) { 18982 ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ 18983 } 18984 18985 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 18986 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; 18987 totalClientFramesProcessed += clientFramesProcessedThisIteration; 18988 18989 /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ 18990 (void)totalClientFramesProcessed; 18991 18992 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { 18993 break; /* We're done. */ 18994 } 18995 } 18996 } 18997 } 18998 18999 static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) 19000 { 19001 ma_result result; 19002 ma_uint32 totalDeviceFramesProcessed = 0; 19003 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; 19004 19005 MA_ASSERT(pDevice != NULL); 19006 MA_ASSERT(frameCountInDeviceFormat > 0); 19007 MA_ASSERT(pFramesInDeviceFormat != NULL); 19008 MA_ASSERT(pRB != NULL); 19009 19010 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ 19011 for (;;) { 19012 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); 19013 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 19014 ma_uint64 framesProcessedInDeviceFormat; 19015 ma_uint64 framesProcessedInClientFormat; 19016 void* pFramesInClientFormat; 19017 19018 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); 19019 if (result != MA_SUCCESS) { 19020 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); 19021 break; 19022 } 19023 19024 if (framesToProcessInClientFormat == 0) { 19025 if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { 19026 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ 19027 } 19028 } 19029 19030 /* Convert. */ 19031 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; 19032 framesProcessedInClientFormat = framesToProcessInClientFormat; 19033 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); 19034 if (result != MA_SUCCESS) { 19035 break; 19036 } 19037 19038 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ 19039 if (result != MA_SUCCESS) { 19040 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); 19041 break; 19042 } 19043 19044 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 19045 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ 19046 19047 /* We're done when we're unable to process any client nor device frames. */ 19048 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { 19049 break; /* Done. */ 19050 } 19051 } 19052 19053 return MA_SUCCESS; 19054 } 19055 19056 static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) 19057 { 19058 ma_result result; 19059 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 19060 ma_uint32 totalFramesReadOut = 0; 19061 19062 MA_ASSERT(pDevice != NULL); 19063 MA_ASSERT(frameCount > 0); 19064 MA_ASSERT(pFramesInInternalFormat != NULL); 19065 MA_ASSERT(pRB != NULL); 19066 MA_ASSERT(pDevice->playback.pInputCache != NULL); 19067 19068 /* 19069 Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for 19070 the whole frameCount frames we just use silence instead for the input data. 19071 */ 19072 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); 19073 19074 while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { 19075 /* 19076 We should have a buffer allocated on the heap. Any playback frames still sitting in there 19077 need to be sent to the internal device before we process any more data from the client. 19078 */ 19079 if (pDevice->playback.inputCacheRemaining > 0) { 19080 ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; 19081 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); 19082 ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); 19083 19084 pDevice->playback.inputCacheConsumed += framesConvertedIn; 19085 pDevice->playback.inputCacheRemaining -= framesConvertedIn; 19086 19087 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ 19088 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 19089 } 19090 19091 /* If there's no more data in the cache we'll need to fill it with some. */ 19092 if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { 19093 ma_uint32 inputFrameCount; 19094 void* pInputFrames; 19095 19096 inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; 19097 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); 19098 if (result == MA_SUCCESS) { 19099 if (inputFrameCount > 0) { 19100 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); 19101 } else { 19102 if (ma_pcm_rb_pointer_distance(pRB) == 0) { 19103 break; /* Underrun. */ 19104 } 19105 } 19106 } else { 19107 /* No capture data available. Feed in silence. */ 19108 inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); 19109 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); 19110 } 19111 19112 pDevice->playback.inputCacheConsumed = 0; 19113 pDevice->playback.inputCacheRemaining = inputFrameCount; 19114 19115 result = ma_pcm_rb_commit_read(pRB, inputFrameCount); 19116 if (result != MA_SUCCESS) { 19117 return result; /* Should never happen. */ 19118 } 19119 } 19120 } 19121 19122 return MA_SUCCESS; 19123 } 19124 19125 /* A helper for changing the state of the device. */ 19126 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) 19127 { 19128 ma_atomic_device_state_set(&pDevice->state, newState); 19129 } 19130 19131 19132 #if defined(MA_WIN32) 19133 static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; 19134 static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; 19135 /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ 19136 /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ 19137 #endif 19138 19139 19140 19141 MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ 19142 { 19143 ma_uint32 i; 19144 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { 19145 if (g_maFormatPriorities[i] == format) { 19146 return i; 19147 } 19148 } 19149 19150 /* Getting here means the format could not be found or is equal to ma_format_unknown. */ 19151 return (ma_uint32)-1; 19152 } 19153 19154 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); 19155 19156 static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) 19157 { 19158 if (pDeviceDescriptor == NULL) { 19159 return MA_FALSE; 19160 } 19161 19162 if (pDeviceDescriptor->format == ma_format_unknown) { 19163 return MA_FALSE; 19164 } 19165 19166 if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { 19167 return MA_FALSE; 19168 } 19169 19170 if (pDeviceDescriptor->sampleRate == 0) { 19171 return MA_FALSE; 19172 } 19173 19174 return MA_TRUE; 19175 } 19176 19177 19178 static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) 19179 { 19180 ma_result result = MA_SUCCESS; 19181 ma_bool32 exitLoop = MA_FALSE; 19182 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 19183 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 19184 ma_uint32 capturedDeviceDataCapInFrames = 0; 19185 ma_uint32 playbackDeviceDataCapInFrames = 0; 19186 19187 MA_ASSERT(pDevice != NULL); 19188 19189 /* Just some quick validation on the device type and the available callbacks. */ 19190 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 19191 if (pDevice->pContext->callbacks.onDeviceRead == NULL) { 19192 return MA_NOT_IMPLEMENTED; 19193 } 19194 19195 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 19196 } 19197 19198 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 19199 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { 19200 return MA_NOT_IMPLEMENTED; 19201 } 19202 19203 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 19204 } 19205 19206 /* NOTE: The device was started outside of this function, in the worker thread. */ 19207 19208 while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { 19209 switch (pDevice->type) { 19210 case ma_device_type_duplex: 19211 { 19212 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ 19213 ma_uint32 totalCapturedDeviceFramesProcessed = 0; 19214 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); 19215 19216 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { 19217 ma_uint32 capturedDeviceFramesRemaining; 19218 ma_uint32 capturedDeviceFramesProcessed; 19219 ma_uint32 capturedDeviceFramesToProcess; 19220 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; 19221 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { 19222 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; 19223 } 19224 19225 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); 19226 if (result != MA_SUCCESS) { 19227 exitLoop = MA_TRUE; 19228 break; 19229 } 19230 19231 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; 19232 capturedDeviceFramesProcessed = 0; 19233 19234 /* At this point we have our captured data in device format and we now need to convert it to client format. */ 19235 for (;;) { 19236 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 19237 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 19238 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 19239 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 19240 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); 19241 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; 19242 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 19243 19244 /* Convert capture data from device format to client format. */ 19245 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); 19246 if (result != MA_SUCCESS) { 19247 break; 19248 } 19249 19250 /* 19251 If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small 19252 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. 19253 */ 19254 if (capturedClientFramesToProcessThisIteration == 0) { 19255 break; 19256 } 19257 19258 ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ 19259 19260 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ 19261 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ 19262 19263 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ 19264 for (;;) { 19265 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; 19266 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; 19267 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); 19268 if (result != MA_SUCCESS) { 19269 break; 19270 } 19271 19272 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ 19273 if (result != MA_SUCCESS) { 19274 exitLoop = MA_TRUE; 19275 break; 19276 } 19277 19278 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ 19279 if (capturedClientFramesToProcessThisIteration == 0) { 19280 break; 19281 } 19282 } 19283 19284 /* In case an error happened from ma_device_write__null()... */ 19285 if (result != MA_SUCCESS) { 19286 exitLoop = MA_TRUE; 19287 break; 19288 } 19289 } 19290 19291 /* Make sure we don't get stuck in the inner loop. */ 19292 if (capturedDeviceFramesProcessed == 0) { 19293 break; 19294 } 19295 19296 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; 19297 } 19298 } break; 19299 19300 case ma_device_type_capture: 19301 case ma_device_type_loopback: 19302 { 19303 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; 19304 ma_uint32 framesReadThisPeriod = 0; 19305 while (framesReadThisPeriod < periodSizeInFrames) { 19306 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; 19307 ma_uint32 framesProcessed; 19308 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; 19309 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { 19310 framesToReadThisIteration = capturedDeviceDataCapInFrames; 19311 } 19312 19313 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); 19314 if (result != MA_SUCCESS) { 19315 exitLoop = MA_TRUE; 19316 break; 19317 } 19318 19319 /* Make sure we don't get stuck in the inner loop. */ 19320 if (framesProcessed == 0) { 19321 break; 19322 } 19323 19324 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); 19325 19326 framesReadThisPeriod += framesProcessed; 19327 } 19328 } break; 19329 19330 case ma_device_type_playback: 19331 { 19332 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ 19333 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; 19334 ma_uint32 framesWrittenThisPeriod = 0; 19335 while (framesWrittenThisPeriod < periodSizeInFrames) { 19336 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; 19337 ma_uint32 framesProcessed; 19338 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; 19339 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { 19340 framesToWriteThisIteration = playbackDeviceDataCapInFrames; 19341 } 19342 19343 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); 19344 19345 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); 19346 if (result != MA_SUCCESS) { 19347 exitLoop = MA_TRUE; 19348 break; 19349 } 19350 19351 /* Make sure we don't get stuck in the inner loop. */ 19352 if (framesProcessed == 0) { 19353 break; 19354 } 19355 19356 framesWrittenThisPeriod += framesProcessed; 19357 } 19358 } break; 19359 19360 /* Should never get here. */ 19361 default: break; 19362 } 19363 } 19364 19365 return result; 19366 } 19367 19368 19369 19370 /******************************************************************************* 19371 19372 Null Backend 19373 19374 *******************************************************************************/ 19375 #ifdef MA_HAS_NULL 19376 19377 #define MA_DEVICE_OP_NONE__NULL 0 19378 #define MA_DEVICE_OP_START__NULL 1 19379 #define MA_DEVICE_OP_SUSPEND__NULL 2 19380 #define MA_DEVICE_OP_KILL__NULL 3 19381 19382 static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) 19383 { 19384 ma_device* pDevice = (ma_device*)pData; 19385 MA_ASSERT(pDevice != NULL); 19386 19387 for (;;) { /* Keep the thread alive until the device is uninitialized. */ 19388 ma_uint32 operation; 19389 19390 /* Wait for an operation to be requested. */ 19391 ma_event_wait(&pDevice->null_device.operationEvent); 19392 19393 /* At this point an event should have been triggered. */ 19394 operation = pDevice->null_device.operation; 19395 19396 /* Starting the device needs to put the thread into a loop. */ 19397 if (operation == MA_DEVICE_OP_START__NULL) { 19398 /* Reset the timer just in case. */ 19399 ma_timer_init(&pDevice->null_device.timer); 19400 19401 /* Getting here means a suspend or kill operation has been requested. */ 19402 pDevice->null_device.operationResult = MA_SUCCESS; 19403 ma_event_signal(&pDevice->null_device.operationCompletionEvent); 19404 ma_semaphore_release(&pDevice->null_device.operationSemaphore); 19405 continue; 19406 } 19407 19408 /* Suspending the device means we need to stop the timer and just continue the loop. */ 19409 if (operation == MA_DEVICE_OP_SUSPEND__NULL) { 19410 /* We need to add the current run time to the prior run time, then reset the timer. */ 19411 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); 19412 ma_timer_init(&pDevice->null_device.timer); 19413 19414 /* We're done. */ 19415 pDevice->null_device.operationResult = MA_SUCCESS; 19416 ma_event_signal(&pDevice->null_device.operationCompletionEvent); 19417 ma_semaphore_release(&pDevice->null_device.operationSemaphore); 19418 continue; 19419 } 19420 19421 /* Killing the device means we need to get out of this loop so that this thread can terminate. */ 19422 if (operation == MA_DEVICE_OP_KILL__NULL) { 19423 pDevice->null_device.operationResult = MA_SUCCESS; 19424 ma_event_signal(&pDevice->null_device.operationCompletionEvent); 19425 ma_semaphore_release(&pDevice->null_device.operationSemaphore); 19426 break; 19427 } 19428 19429 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ 19430 if (operation == MA_DEVICE_OP_NONE__NULL) { 19431 MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ 19432 pDevice->null_device.operationResult = MA_INVALID_OPERATION; 19433 ma_event_signal(&pDevice->null_device.operationCompletionEvent); 19434 ma_semaphore_release(&pDevice->null_device.operationSemaphore); 19435 continue; /* Continue the loop. Don't terminate. */ 19436 } 19437 } 19438 19439 return (ma_thread_result)0; 19440 } 19441 19442 static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) 19443 { 19444 ma_result result; 19445 19446 /* 19447 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation 19448 for this was to just post the event to a queue and return immediately, but that has since changed 19449 and now this function is synchronous. I think this can be simplified to just use a mutex. 19450 */ 19451 19452 /* 19453 The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later 19454 to support queing of operations. 19455 */ 19456 result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); 19457 if (result != MA_SUCCESS) { 19458 return result; /* Failed to wait for the event. */ 19459 } 19460 19461 /* 19462 When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to 19463 signal an event to the worker thread to let it know that it can start work. 19464 */ 19465 pDevice->null_device.operation = operation; 19466 19467 /* Once the operation code has been set, the worker thread can start work. */ 19468 if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { 19469 return MA_ERROR; 19470 } 19471 19472 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ 19473 if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { 19474 return MA_ERROR; 19475 } 19476 19477 return pDevice->null_device.operationResult; 19478 } 19479 19480 static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) 19481 { 19482 ma_uint32 internalSampleRate; 19483 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 19484 internalSampleRate = pDevice->capture.internalSampleRate; 19485 } else { 19486 internalSampleRate = pDevice->playback.internalSampleRate; 19487 } 19488 19489 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); 19490 } 19491 19492 static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 19493 { 19494 ma_bool32 cbResult = MA_TRUE; 19495 19496 MA_ASSERT(pContext != NULL); 19497 MA_ASSERT(callback != NULL); 19498 19499 /* Playback. */ 19500 if (cbResult) { 19501 ma_device_info deviceInfo; 19502 MA_ZERO_OBJECT(&deviceInfo); 19503 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); 19504 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ 19505 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 19506 } 19507 19508 /* Capture. */ 19509 if (cbResult) { 19510 ma_device_info deviceInfo; 19511 MA_ZERO_OBJECT(&deviceInfo); 19512 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); 19513 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ 19514 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 19515 } 19516 19517 (void)cbResult; /* Silence a static analysis warning. */ 19518 19519 return MA_SUCCESS; 19520 } 19521 19522 static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 19523 { 19524 MA_ASSERT(pContext != NULL); 19525 19526 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { 19527 return MA_NO_DEVICE; /* Don't know the device. */ 19528 } 19529 19530 /* Name / Description */ 19531 if (deviceType == ma_device_type_playback) { 19532 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); 19533 } else { 19534 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); 19535 } 19536 19537 pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ 19538 19539 /* Support everything on the null backend. */ 19540 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; 19541 pDeviceInfo->nativeDataFormats[0].channels = 0; 19542 pDeviceInfo->nativeDataFormats[0].sampleRate = 0; 19543 pDeviceInfo->nativeDataFormats[0].flags = 0; 19544 pDeviceInfo->nativeDataFormatCount = 1; 19545 19546 (void)pContext; 19547 return MA_SUCCESS; 19548 } 19549 19550 19551 static ma_result ma_device_uninit__null(ma_device* pDevice) 19552 { 19553 MA_ASSERT(pDevice != NULL); 19554 19555 /* Keep it clean and wait for the device thread to finish before returning. */ 19556 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); 19557 19558 /* Wait for the thread to finish before continuing. */ 19559 ma_thread_wait(&pDevice->null_device.deviceThread); 19560 19561 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ 19562 ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); 19563 ma_event_uninit(&pDevice->null_device.operationCompletionEvent); 19564 ma_event_uninit(&pDevice->null_device.operationEvent); 19565 19566 return MA_SUCCESS; 19567 } 19568 19569 static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 19570 { 19571 ma_result result; 19572 19573 MA_ASSERT(pDevice != NULL); 19574 19575 MA_ZERO_OBJECT(&pDevice->null_device); 19576 19577 if (pConfig->deviceType == ma_device_type_loopback) { 19578 return MA_DEVICE_TYPE_NOT_SUPPORTED; 19579 } 19580 19581 /* The null backend supports everything exactly as we specify it. */ 19582 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 19583 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; 19584 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; 19585 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; 19586 19587 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { 19588 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); 19589 } 19590 19591 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); 19592 } 19593 19594 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 19595 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; 19596 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; 19597 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; 19598 19599 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { 19600 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); 19601 } 19602 19603 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); 19604 } 19605 19606 /* 19607 In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the 19608 first period is "written" to it, and then stopped in ma_device_stop__null(). 19609 */ 19610 result = ma_event_init(&pDevice->null_device.operationEvent); 19611 if (result != MA_SUCCESS) { 19612 return result; 19613 } 19614 19615 result = ma_event_init(&pDevice->null_device.operationCompletionEvent); 19616 if (result != MA_SUCCESS) { 19617 return result; 19618 } 19619 19620 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ 19621 if (result != MA_SUCCESS) { 19622 return result; 19623 } 19624 19625 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); 19626 if (result != MA_SUCCESS) { 19627 return result; 19628 } 19629 19630 return MA_SUCCESS; 19631 } 19632 19633 static ma_result ma_device_start__null(ma_device* pDevice) 19634 { 19635 MA_ASSERT(pDevice != NULL); 19636 19637 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); 19638 19639 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); 19640 return MA_SUCCESS; 19641 } 19642 19643 static ma_result ma_device_stop__null(ma_device* pDevice) 19644 { 19645 MA_ASSERT(pDevice != NULL); 19646 19647 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); 19648 19649 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); 19650 return MA_SUCCESS; 19651 } 19652 19653 static ma_bool32 ma_device_is_started__null(ma_device* pDevice) 19654 { 19655 MA_ASSERT(pDevice != NULL); 19656 19657 return ma_atomic_bool32_get(&pDevice->null_device.isStarted); 19658 } 19659 19660 static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 19661 { 19662 ma_result result = MA_SUCCESS; 19663 ma_uint32 totalPCMFramesProcessed; 19664 ma_bool32 wasStartedOnEntry; 19665 19666 if (pFramesWritten != NULL) { 19667 *pFramesWritten = 0; 19668 } 19669 19670 wasStartedOnEntry = ma_device_is_started__null(pDevice); 19671 19672 /* Keep going until everything has been read. */ 19673 totalPCMFramesProcessed = 0; 19674 while (totalPCMFramesProcessed < frameCount) { 19675 ma_uint64 targetFrame; 19676 19677 /* If there are any frames remaining in the current period, consume those first. */ 19678 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { 19679 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); 19680 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; 19681 if (framesToProcess > framesRemaining) { 19682 framesToProcess = framesRemaining; 19683 } 19684 19685 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ 19686 (void)pPCMFrames; 19687 19688 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; 19689 totalPCMFramesProcessed += framesToProcess; 19690 } 19691 19692 /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ 19693 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { 19694 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; 19695 19696 if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { 19697 result = ma_device_start__null(pDevice); 19698 if (result != MA_SUCCESS) { 19699 break; 19700 } 19701 } 19702 } 19703 19704 /* If we've consumed the whole buffer we can return now. */ 19705 MA_ASSERT(totalPCMFramesProcessed <= frameCount); 19706 if (totalPCMFramesProcessed == frameCount) { 19707 break; 19708 } 19709 19710 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ 19711 targetFrame = pDevice->null_device.lastProcessedFramePlayback; 19712 for (;;) { 19713 ma_uint64 currentFrame; 19714 19715 /* Stop waiting if the device has been stopped. */ 19716 if (!ma_device_is_started__null(pDevice)) { 19717 break; 19718 } 19719 19720 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); 19721 if (currentFrame >= targetFrame) { 19722 break; 19723 } 19724 19725 /* Getting here means we haven't yet reached the target sample, so continue waiting. */ 19726 ma_sleep(10); 19727 } 19728 19729 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; 19730 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; 19731 } 19732 19733 if (pFramesWritten != NULL) { 19734 *pFramesWritten = totalPCMFramesProcessed; 19735 } 19736 19737 return result; 19738 } 19739 19740 static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 19741 { 19742 ma_result result = MA_SUCCESS; 19743 ma_uint32 totalPCMFramesProcessed; 19744 19745 if (pFramesRead != NULL) { 19746 *pFramesRead = 0; 19747 } 19748 19749 /* Keep going until everything has been read. */ 19750 totalPCMFramesProcessed = 0; 19751 while (totalPCMFramesProcessed < frameCount) { 19752 ma_uint64 targetFrame; 19753 19754 /* If there are any frames remaining in the current period, consume those first. */ 19755 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { 19756 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 19757 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); 19758 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; 19759 if (framesToProcess > framesRemaining) { 19760 framesToProcess = framesRemaining; 19761 } 19762 19763 /* We need to ensure the output buffer is zeroed. */ 19764 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); 19765 19766 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; 19767 totalPCMFramesProcessed += framesToProcess; 19768 } 19769 19770 /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ 19771 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { 19772 pDevice->null_device.currentPeriodFramesRemainingCapture = 0; 19773 } 19774 19775 /* If we've consumed the whole buffer we can return now. */ 19776 MA_ASSERT(totalPCMFramesProcessed <= frameCount); 19777 if (totalPCMFramesProcessed == frameCount) { 19778 break; 19779 } 19780 19781 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ 19782 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; 19783 for (;;) { 19784 ma_uint64 currentFrame; 19785 19786 /* Stop waiting if the device has been stopped. */ 19787 if (!ma_device_is_started__null(pDevice)) { 19788 break; 19789 } 19790 19791 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); 19792 if (currentFrame >= targetFrame) { 19793 break; 19794 } 19795 19796 /* Getting here means we haven't yet reached the target sample, so continue waiting. */ 19797 ma_sleep(10); 19798 } 19799 19800 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; 19801 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; 19802 } 19803 19804 if (pFramesRead != NULL) { 19805 *pFramesRead = totalPCMFramesProcessed; 19806 } 19807 19808 return result; 19809 } 19810 19811 static ma_result ma_context_uninit__null(ma_context* pContext) 19812 { 19813 MA_ASSERT(pContext != NULL); 19814 MA_ASSERT(pContext->backend == ma_backend_null); 19815 19816 (void)pContext; 19817 return MA_SUCCESS; 19818 } 19819 19820 static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 19821 { 19822 MA_ASSERT(pContext != NULL); 19823 19824 (void)pConfig; 19825 (void)pContext; 19826 19827 pCallbacks->onContextInit = ma_context_init__null; 19828 pCallbacks->onContextUninit = ma_context_uninit__null; 19829 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; 19830 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; 19831 pCallbacks->onDeviceInit = ma_device_init__null; 19832 pCallbacks->onDeviceUninit = ma_device_uninit__null; 19833 pCallbacks->onDeviceStart = ma_device_start__null; 19834 pCallbacks->onDeviceStop = ma_device_stop__null; 19835 pCallbacks->onDeviceRead = ma_device_read__null; 19836 pCallbacks->onDeviceWrite = ma_device_write__null; 19837 pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ 19838 19839 /* The null backend always works. */ 19840 return MA_SUCCESS; 19841 } 19842 #endif 19843 19844 19845 19846 /******************************************************************************* 19847 19848 WIN32 COMMON 19849 19850 *******************************************************************************/ 19851 #if defined(MA_WIN32) 19852 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 19853 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) 19854 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() 19855 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) 19856 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) 19857 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) 19858 #else 19859 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) 19860 #define ma_CoUninitialize(pContext) CoUninitialize() 19861 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) 19862 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) 19863 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) 19864 #endif 19865 19866 #if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) 19867 typedef size_t DWORD_PTR; 19868 #endif 19869 19870 #if !defined(WAVE_FORMAT_1M08) 19871 #define WAVE_FORMAT_1M08 0x00000001 19872 #define WAVE_FORMAT_1S08 0x00000002 19873 #define WAVE_FORMAT_1M16 0x00000004 19874 #define WAVE_FORMAT_1S16 0x00000008 19875 #define WAVE_FORMAT_2M08 0x00000010 19876 #define WAVE_FORMAT_2S08 0x00000020 19877 #define WAVE_FORMAT_2M16 0x00000040 19878 #define WAVE_FORMAT_2S16 0x00000080 19879 #define WAVE_FORMAT_4M08 0x00000100 19880 #define WAVE_FORMAT_4S08 0x00000200 19881 #define WAVE_FORMAT_4M16 0x00000400 19882 #define WAVE_FORMAT_4S16 0x00000800 19883 #endif 19884 19885 #if !defined(WAVE_FORMAT_44M08) 19886 #define WAVE_FORMAT_44M08 0x00000100 19887 #define WAVE_FORMAT_44S08 0x00000200 19888 #define WAVE_FORMAT_44M16 0x00000400 19889 #define WAVE_FORMAT_44S16 0x00000800 19890 #define WAVE_FORMAT_48M08 0x00001000 19891 #define WAVE_FORMAT_48S08 0x00002000 19892 #define WAVE_FORMAT_48M16 0x00004000 19893 #define WAVE_FORMAT_48S16 0x00008000 19894 #define WAVE_FORMAT_96M08 0x00010000 19895 #define WAVE_FORMAT_96S08 0x00020000 19896 #define WAVE_FORMAT_96M16 0x00040000 19897 #define WAVE_FORMAT_96S16 0x00080000 19898 #endif 19899 19900 #ifndef SPEAKER_FRONT_LEFT 19901 #define SPEAKER_FRONT_LEFT 0x1 19902 #define SPEAKER_FRONT_RIGHT 0x2 19903 #define SPEAKER_FRONT_CENTER 0x4 19904 #define SPEAKER_LOW_FREQUENCY 0x8 19905 #define SPEAKER_BACK_LEFT 0x10 19906 #define SPEAKER_BACK_RIGHT 0x20 19907 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 19908 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 19909 #define SPEAKER_BACK_CENTER 0x100 19910 #define SPEAKER_SIDE_LEFT 0x200 19911 #define SPEAKER_SIDE_RIGHT 0x400 19912 #define SPEAKER_TOP_CENTER 0x800 19913 #define SPEAKER_TOP_FRONT_LEFT 0x1000 19914 #define SPEAKER_TOP_FRONT_CENTER 0x2000 19915 #define SPEAKER_TOP_FRONT_RIGHT 0x4000 19916 #define SPEAKER_TOP_BACK_LEFT 0x8000 19917 #define SPEAKER_TOP_BACK_CENTER 0x10000 19918 #define SPEAKER_TOP_BACK_RIGHT 0x20000 19919 #endif 19920 19921 /* 19922 Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this 19923 because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The 19924 standard version uses tight packing, but for compiler compatibility we're not doing that with ours. 19925 */ 19926 typedef struct 19927 { 19928 WORD wFormatTag; 19929 WORD nChannels; 19930 DWORD nSamplesPerSec; 19931 DWORD nAvgBytesPerSec; 19932 WORD nBlockAlign; 19933 WORD wBitsPerSample; 19934 WORD cbSize; 19935 } MA_WAVEFORMATEX; 19936 19937 typedef struct 19938 { 19939 WORD wFormatTag; 19940 WORD nChannels; 19941 DWORD nSamplesPerSec; 19942 DWORD nAvgBytesPerSec; 19943 WORD nBlockAlign; 19944 WORD wBitsPerSample; 19945 WORD cbSize; 19946 union 19947 { 19948 WORD wValidBitsPerSample; 19949 WORD wSamplesPerBlock; 19950 WORD wReserved; 19951 } Samples; 19952 DWORD dwChannelMask; 19953 GUID SubFormat; 19954 } MA_WAVEFORMATEXTENSIBLE; 19955 19956 19957 19958 #ifndef WAVE_FORMAT_EXTENSIBLE 19959 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE 19960 #endif 19961 19962 #ifndef WAVE_FORMAT_PCM 19963 #define WAVE_FORMAT_PCM 1 19964 #endif 19965 19966 #ifndef WAVE_FORMAT_IEEE_FLOAT 19967 #define WAVE_FORMAT_IEEE_FLOAT 0x0003 19968 #endif 19969 19970 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ 19971 static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) 19972 { 19973 switch (id) 19974 { 19975 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; 19976 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; 19977 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; 19978 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; 19979 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; 19980 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; 19981 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; 19982 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; 19983 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; 19984 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; 19985 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; 19986 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; 19987 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; 19988 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; 19989 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; 19990 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; 19991 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; 19992 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; 19993 default: return 0; 19994 } 19995 } 19996 19997 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ 19998 static DWORD ma_channel_id_to_win32(DWORD id) 19999 { 20000 switch (id) 20001 { 20002 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; 20003 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; 20004 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; 20005 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; 20006 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; 20007 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; 20008 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; 20009 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; 20010 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; 20011 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; 20012 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; 20013 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; 20014 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; 20015 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; 20016 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; 20017 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; 20018 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; 20019 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; 20020 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; 20021 default: return 0; 20022 } 20023 } 20024 20025 /* Converts a channel mapping to a Win32-style channel mask. */ 20026 static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) 20027 { 20028 DWORD dwChannelMask = 0; 20029 ma_uint32 iChannel; 20030 20031 for (iChannel = 0; iChannel < channels; ++iChannel) { 20032 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); 20033 } 20034 20035 return dwChannelMask; 20036 } 20037 20038 /* Converts a Win32-style channel mask to a miniaudio channel map. */ 20039 static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) 20040 { 20041 /* If the channel mask is set to 0, just assume a default Win32 channel map. */ 20042 if (dwChannelMask == 0) { 20043 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); 20044 } else { 20045 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { 20046 pChannelMap[0] = MA_CHANNEL_MONO; 20047 } else { 20048 /* Just iterate over each bit. */ 20049 ma_uint32 iChannel = 0; 20050 ma_uint32 iBit; 20051 20052 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { 20053 DWORD bitValue = (dwChannelMask & (1UL << iBit)); 20054 if (bitValue != 0) { 20055 /* The bit is set. */ 20056 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); 20057 iChannel += 1; 20058 } 20059 } 20060 } 20061 } 20062 } 20063 20064 #ifdef __cplusplus 20065 static ma_bool32 ma_is_guid_equal(const void* a, const void* b) 20066 { 20067 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); 20068 } 20069 #else 20070 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) 20071 #endif 20072 20073 static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) 20074 { 20075 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 20076 return ma_is_guid_equal(guid, &nullguid); 20077 } 20078 20079 static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) 20080 { 20081 MA_ASSERT(pWF != NULL); 20082 20083 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { 20084 const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; 20085 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { 20086 if (pWFEX->Samples.wValidBitsPerSample == 32) { 20087 return ma_format_s32; 20088 } 20089 if (pWFEX->Samples.wValidBitsPerSample == 24) { 20090 if (pWFEX->wBitsPerSample == 32) { 20091 return ma_format_s32; 20092 } 20093 if (pWFEX->wBitsPerSample == 24) { 20094 return ma_format_s24; 20095 } 20096 } 20097 if (pWFEX->Samples.wValidBitsPerSample == 16) { 20098 return ma_format_s16; 20099 } 20100 if (pWFEX->Samples.wValidBitsPerSample == 8) { 20101 return ma_format_u8; 20102 } 20103 } 20104 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { 20105 if (pWFEX->Samples.wValidBitsPerSample == 32) { 20106 return ma_format_f32; 20107 } 20108 /* 20109 if (pWFEX->Samples.wValidBitsPerSample == 64) { 20110 return ma_format_f64; 20111 } 20112 */ 20113 } 20114 } else { 20115 if (pWF->wFormatTag == WAVE_FORMAT_PCM) { 20116 if (pWF->wBitsPerSample == 32) { 20117 return ma_format_s32; 20118 } 20119 if (pWF->wBitsPerSample == 24) { 20120 return ma_format_s24; 20121 } 20122 if (pWF->wBitsPerSample == 16) { 20123 return ma_format_s16; 20124 } 20125 if (pWF->wBitsPerSample == 8) { 20126 return ma_format_u8; 20127 } 20128 } 20129 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { 20130 if (pWF->wBitsPerSample == 32) { 20131 return ma_format_f32; 20132 } 20133 if (pWF->wBitsPerSample == 64) { 20134 /*return ma_format_f64;*/ 20135 } 20136 } 20137 } 20138 20139 return ma_format_unknown; 20140 } 20141 #endif 20142 20143 20144 /******************************************************************************* 20145 20146 WASAPI Backend 20147 20148 *******************************************************************************/ 20149 #ifdef MA_HAS_WASAPI 20150 #if 0 20151 #if defined(_MSC_VER) 20152 #pragma warning(push) 20153 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ 20154 #endif 20155 #include <audioclient.h> 20156 #include <mmdeviceapi.h> 20157 #if defined(_MSC_VER) 20158 #pragma warning(pop) 20159 #endif 20160 #endif /* 0 */ 20161 20162 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); 20163 20164 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ 20165 #define MA_WIN32_WINNT_VISTA 0x0600 20166 #define MA_VER_MINORVERSION 0x01 20167 #define MA_VER_MAJORVERSION 0x02 20168 #define MA_VER_SERVICEPACKMAJOR 0x20 20169 #define MA_VER_GREATER_EQUAL 0x03 20170 20171 typedef struct { 20172 DWORD dwOSVersionInfoSize; 20173 DWORD dwMajorVersion; 20174 DWORD dwMinorVersion; 20175 DWORD dwBuildNumber; 20176 DWORD dwPlatformId; 20177 WCHAR szCSDVersion[128]; 20178 WORD wServicePackMajor; 20179 WORD wServicePackMinor; 20180 WORD wSuiteMask; 20181 BYTE wProductType; 20182 BYTE wReserved; 20183 } ma_OSVERSIONINFOEXW; 20184 20185 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); 20186 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); 20187 20188 20189 #ifndef PROPERTYKEY_DEFINED 20190 #define PROPERTYKEY_DEFINED 20191 #ifndef __WATCOMC__ 20192 typedef struct 20193 { 20194 GUID fmtid; 20195 DWORD pid; 20196 } PROPERTYKEY; 20197 #endif 20198 #endif 20199 20200 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ 20201 static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) 20202 { 20203 MA_ZERO_OBJECT(pProp); 20204 } 20205 20206 20207 static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; 20208 static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; 20209 20210 static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ 20211 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) 20212 static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ 20213 #endif 20214 20215 static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ 20216 static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ 20217 static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ 20218 static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ 20219 static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ 20220 static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ 20221 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) 20222 static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ 20223 static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ 20224 static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ 20225 #endif 20226 20227 static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ 20228 static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ 20229 20230 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 20231 #define MA_MM_DEVICE_STATE_ACTIVE 1 20232 #define MA_MM_DEVICE_STATE_DISABLED 2 20233 #define MA_MM_DEVICE_STATE_NOTPRESENT 4 20234 #define MA_MM_DEVICE_STATE_UNPLUGGED 8 20235 20236 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; 20237 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; 20238 typedef struct ma_IMMDevice ma_IMMDevice; 20239 #else 20240 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; 20241 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; 20242 #endif 20243 typedef struct ma_IPropertyStore ma_IPropertyStore; 20244 typedef struct ma_IAudioClient ma_IAudioClient; 20245 typedef struct ma_IAudioClient2 ma_IAudioClient2; 20246 typedef struct ma_IAudioClient3 ma_IAudioClient3; 20247 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; 20248 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; 20249 20250 typedef ma_int64 MA_REFERENCE_TIME; 20251 20252 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 20253 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 20254 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 20255 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 20256 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 20257 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 20258 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 20259 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 20260 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 20261 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 20262 20263 /* Buffer flags. */ 20264 #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 20265 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 20266 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 20267 20268 typedef enum 20269 { 20270 ma_eRender = 0, 20271 ma_eCapture = 1, 20272 ma_eAll = 2 20273 } ma_EDataFlow; 20274 20275 typedef enum 20276 { 20277 ma_eConsole = 0, 20278 ma_eMultimedia = 1, 20279 ma_eCommunications = 2 20280 } ma_ERole; 20281 20282 typedef enum 20283 { 20284 MA_AUDCLNT_SHAREMODE_SHARED, 20285 MA_AUDCLNT_SHAREMODE_EXCLUSIVE 20286 } MA_AUDCLNT_SHAREMODE; 20287 20288 typedef enum 20289 { 20290 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ 20291 } MA_AUDIO_STREAM_CATEGORY; 20292 20293 typedef struct 20294 { 20295 ma_uint32 cbSize; 20296 BOOL bIsOffload; 20297 MA_AUDIO_STREAM_CATEGORY eCategory; 20298 } ma_AudioClientProperties; 20299 20300 /* IUnknown */ 20301 typedef struct 20302 { 20303 /* IUnknown */ 20304 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); 20305 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); 20306 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); 20307 } ma_IUnknownVtbl; 20308 struct ma_IUnknown 20309 { 20310 ma_IUnknownVtbl* lpVtbl; 20311 }; 20312 static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20313 static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20314 static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } 20315 20316 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 20317 /* IMMNotificationClient */ 20318 typedef struct 20319 { 20320 /* IUnknown */ 20321 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); 20322 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); 20323 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); 20324 20325 /* IMMNotificationClient */ 20326 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); 20327 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); 20328 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); 20329 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); 20330 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); 20331 } ma_IMMNotificationClientVtbl; 20332 20333 /* IMMDeviceEnumerator */ 20334 typedef struct 20335 { 20336 /* IUnknown */ 20337 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); 20338 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); 20339 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); 20340 20341 /* IMMDeviceEnumerator */ 20342 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); 20343 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); 20344 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); 20345 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); 20346 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); 20347 } ma_IMMDeviceEnumeratorVtbl; 20348 struct ma_IMMDeviceEnumerator 20349 { 20350 ma_IMMDeviceEnumeratorVtbl* lpVtbl; 20351 }; 20352 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20353 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20354 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } 20355 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } 20356 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } 20357 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } 20358 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } 20359 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } 20360 20361 20362 /* IMMDeviceCollection */ 20363 typedef struct 20364 { 20365 /* IUnknown */ 20366 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); 20367 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); 20368 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); 20369 20370 /* IMMDeviceCollection */ 20371 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); 20372 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); 20373 } ma_IMMDeviceCollectionVtbl; 20374 struct ma_IMMDeviceCollection 20375 { 20376 ma_IMMDeviceCollectionVtbl* lpVtbl; 20377 }; 20378 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20379 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20380 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } 20381 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } 20382 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } 20383 20384 20385 /* IMMDevice */ 20386 typedef struct 20387 { 20388 /* IUnknown */ 20389 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); 20390 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); 20391 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); 20392 20393 /* IMMDevice */ 20394 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); 20395 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); 20396 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); 20397 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); 20398 } ma_IMMDeviceVtbl; 20399 struct ma_IMMDevice 20400 { 20401 ma_IMMDeviceVtbl* lpVtbl; 20402 }; 20403 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20404 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20405 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } 20406 static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } 20407 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } 20408 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } 20409 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } 20410 #else 20411 /* IActivateAudioInterfaceAsyncOperation */ 20412 typedef struct 20413 { 20414 /* IUnknown */ 20415 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); 20416 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); 20417 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); 20418 20419 /* IActivateAudioInterfaceAsyncOperation */ 20420 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); 20421 } ma_IActivateAudioInterfaceAsyncOperationVtbl; 20422 struct ma_IActivateAudioInterfaceAsyncOperation 20423 { 20424 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; 20425 }; 20426 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20427 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20428 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } 20429 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } 20430 #endif 20431 20432 /* IPropertyStore */ 20433 typedef struct 20434 { 20435 /* IUnknown */ 20436 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); 20437 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); 20438 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); 20439 20440 /* IPropertyStore */ 20441 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); 20442 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); 20443 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); 20444 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); 20445 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); 20446 } ma_IPropertyStoreVtbl; 20447 struct ma_IPropertyStore 20448 { 20449 ma_IPropertyStoreVtbl* lpVtbl; 20450 }; 20451 static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20452 static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20453 static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } 20454 static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } 20455 static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } 20456 static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } 20457 static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } 20458 static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } 20459 20460 20461 /* IAudioClient */ 20462 typedef struct 20463 { 20464 /* IUnknown */ 20465 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); 20466 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); 20467 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); 20468 20469 /* IAudioClient */ 20470 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); 20471 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); 20472 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); 20473 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); 20474 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); 20475 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); 20476 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); 20477 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); 20478 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); 20479 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); 20480 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); 20481 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); 20482 } ma_IAudioClientVtbl; 20483 struct ma_IAudioClient 20484 { 20485 ma_IAudioClientVtbl* lpVtbl; 20486 }; 20487 static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20488 static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20489 static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } 20490 static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } 20491 static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } 20492 static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } 20493 static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } 20494 static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } 20495 static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } 20496 static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } 20497 static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } 20498 static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } 20499 static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } 20500 static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } 20501 static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } 20502 20503 /* IAudioClient2 */ 20504 typedef struct 20505 { 20506 /* IUnknown */ 20507 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); 20508 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); 20509 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); 20510 20511 /* IAudioClient */ 20512 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); 20513 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); 20514 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); 20515 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); 20516 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); 20517 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); 20518 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); 20519 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); 20520 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); 20521 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); 20522 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); 20523 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); 20524 20525 /* IAudioClient2 */ 20526 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); 20527 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); 20528 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); 20529 } ma_IAudioClient2Vtbl; 20530 struct ma_IAudioClient2 20531 { 20532 ma_IAudioClient2Vtbl* lpVtbl; 20533 }; 20534 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20535 static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20536 static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } 20537 static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } 20538 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } 20539 static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } 20540 static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } 20541 static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } 20542 static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } 20543 static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } 20544 static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } 20545 static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } 20546 static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } 20547 static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } 20548 static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } 20549 static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } 20550 static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } 20551 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } 20552 20553 20554 /* IAudioClient3 */ 20555 typedef struct 20556 { 20557 /* IUnknown */ 20558 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); 20559 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); 20560 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); 20561 20562 /* IAudioClient */ 20563 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); 20564 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); 20565 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); 20566 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); 20567 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); 20568 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); 20569 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); 20570 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); 20571 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); 20572 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); 20573 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); 20574 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); 20575 20576 /* IAudioClient2 */ 20577 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); 20578 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); 20579 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); 20580 20581 /* IAudioClient3 */ 20582 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); 20583 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); 20584 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); 20585 } ma_IAudioClient3Vtbl; 20586 struct ma_IAudioClient3 20587 { 20588 ma_IAudioClient3Vtbl* lpVtbl; 20589 }; 20590 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20591 static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20592 static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } 20593 static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } 20594 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } 20595 static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } 20596 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } 20597 static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } 20598 static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } 20599 static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } 20600 static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } 20601 static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } 20602 static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } 20603 static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } 20604 static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } 20605 static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } 20606 static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } 20607 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } 20608 static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } 20609 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } 20610 static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } 20611 20612 20613 /* IAudioRenderClient */ 20614 typedef struct 20615 { 20616 /* IUnknown */ 20617 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); 20618 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); 20619 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); 20620 20621 /* IAudioRenderClient */ 20622 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); 20623 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); 20624 } ma_IAudioRenderClientVtbl; 20625 struct ma_IAudioRenderClient 20626 { 20627 ma_IAudioRenderClientVtbl* lpVtbl; 20628 }; 20629 static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20630 static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20631 static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } 20632 static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } 20633 static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } 20634 20635 20636 /* IAudioCaptureClient */ 20637 typedef struct 20638 { 20639 /* IUnknown */ 20640 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); 20641 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); 20642 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); 20643 20644 /* IAudioRenderClient */ 20645 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); 20646 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); 20647 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); 20648 } ma_IAudioCaptureClientVtbl; 20649 struct ma_IAudioCaptureClient 20650 { 20651 ma_IAudioCaptureClientVtbl* lpVtbl; 20652 }; 20653 static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 20654 static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } 20655 static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } 20656 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } 20657 static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } 20658 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } 20659 20660 #if defined(MA_WIN32_UWP) 20661 /* mmdevapi Functions */ 20662 typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); 20663 #endif 20664 20665 /* Avrt Functions */ 20666 typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); 20667 typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); 20668 20669 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) 20670 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; 20671 20672 typedef struct 20673 { 20674 /* IUnknown */ 20675 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); 20676 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); 20677 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); 20678 20679 /* IActivateAudioInterfaceCompletionHandler */ 20680 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); 20681 } ma_completion_handler_uwp_vtbl; 20682 struct ma_completion_handler_uwp 20683 { 20684 ma_completion_handler_uwp_vtbl* lpVtbl; 20685 MA_ATOMIC(4, ma_uint32) counter; 20686 HANDLE hEvent; 20687 }; 20688 20689 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) 20690 { 20691 /* 20692 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To 20693 "implement" this, we just make sure we return pThis when the IAgileObject is requested. 20694 */ 20695 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { 20696 *ppObject = NULL; 20697 return E_NOINTERFACE; 20698 } 20699 20700 /* Getting here means the IID is IUnknown or IMMNotificationClient. */ 20701 *ppObject = (void*)pThis; 20702 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); 20703 return S_OK; 20704 } 20705 20706 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) 20707 { 20708 return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; 20709 } 20710 20711 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) 20712 { 20713 ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; 20714 if (newRefCount == 0) { 20715 return 0; /* We don't free anything here because we never allocate the object on the heap. */ 20716 } 20717 20718 return (ULONG)newRefCount; 20719 } 20720 20721 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) 20722 { 20723 (void)pActivateOperation; 20724 SetEvent(pThis->hEvent); 20725 return S_OK; 20726 } 20727 20728 20729 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { 20730 ma_completion_handler_uwp_QueryInterface, 20731 ma_completion_handler_uwp_AddRef, 20732 ma_completion_handler_uwp_Release, 20733 ma_completion_handler_uwp_ActivateCompleted 20734 }; 20735 20736 static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) 20737 { 20738 MA_ASSERT(pHandler != NULL); 20739 MA_ZERO_OBJECT(pHandler); 20740 20741 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; 20742 pHandler->counter = 1; 20743 pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); 20744 if (pHandler->hEvent == NULL) { 20745 return ma_result_from_GetLastError(GetLastError()); 20746 } 20747 20748 return MA_SUCCESS; 20749 } 20750 20751 static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) 20752 { 20753 if (pHandler->hEvent != NULL) { 20754 CloseHandle(pHandler->hEvent); 20755 } 20756 } 20757 20758 static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) 20759 { 20760 WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); 20761 } 20762 #endif /* !MA_WIN32_DESKTOP */ 20763 20764 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ 20765 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 20766 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) 20767 { 20768 /* 20769 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else 20770 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. 20771 */ 20772 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { 20773 *ppObject = NULL; 20774 return E_NOINTERFACE; 20775 } 20776 20777 /* Getting here means the IID is IUnknown or IMMNotificationClient. */ 20778 *ppObject = (void*)pThis; 20779 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); 20780 return S_OK; 20781 } 20782 20783 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) 20784 { 20785 return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; 20786 } 20787 20788 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) 20789 { 20790 ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; 20791 if (newRefCount == 0) { 20792 return 0; /* We don't free anything here because we never allocate the object on the heap. */ 20793 } 20794 20795 return (ULONG)newRefCount; 20796 } 20797 20798 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) 20799 { 20800 ma_bool32 isThisDevice = MA_FALSE; 20801 ma_bool32 isCapture = MA_FALSE; 20802 ma_bool32 isPlayback = MA_FALSE; 20803 20804 #ifdef MA_DEBUG_OUTPUT 20805 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ 20806 #endif 20807 20808 /* 20809 There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect 20810 that the device is disabled or has been unplugged. 20811 */ 20812 if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { 20813 isCapture = MA_TRUE; 20814 if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { 20815 isThisDevice = MA_TRUE; 20816 } 20817 } 20818 20819 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { 20820 isPlayback = MA_TRUE; 20821 if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { 20822 isThisDevice = MA_TRUE; 20823 } 20824 } 20825 20826 20827 /* 20828 If the device ID matches our device we need to mark our device as detached and stop it. When a 20829 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device 20830 was started at the time of being removed. 20831 */ 20832 if (isThisDevice) { 20833 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { 20834 /* 20835 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll 20836 use this to determine whether or not we need to automatically start the device when it's 20837 plugged back in again. 20838 */ 20839 if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { 20840 if (isPlayback) { 20841 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; 20842 } 20843 if (isCapture) { 20844 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; 20845 } 20846 20847 ma_device_stop(pThis->pDevice); 20848 } 20849 } 20850 20851 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { 20852 /* The device was activated. If we were detached, we need to start it again. */ 20853 ma_bool8 tryRestartingDevice = MA_FALSE; 20854 20855 if (isPlayback) { 20856 if (pThis->pDevice->wasapi.isDetachedPlayback) { 20857 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; 20858 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); 20859 tryRestartingDevice = MA_TRUE; 20860 } 20861 } 20862 20863 if (isCapture) { 20864 if (pThis->pDevice->wasapi.isDetachedCapture) { 20865 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; 20866 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); 20867 tryRestartingDevice = MA_TRUE; 20868 } 20869 } 20870 20871 if (tryRestartingDevice) { 20872 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { 20873 ma_device_start(pThis->pDevice); 20874 } 20875 } 20876 } 20877 } 20878 20879 return S_OK; 20880 } 20881 20882 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) 20883 { 20884 #ifdef MA_DEBUG_OUTPUT 20885 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ 20886 #endif 20887 20888 /* We don't need to worry about this event for our purposes. */ 20889 (void)pThis; 20890 (void)pDeviceID; 20891 return S_OK; 20892 } 20893 20894 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) 20895 { 20896 #ifdef MA_DEBUG_OUTPUT 20897 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ 20898 #endif 20899 20900 /* We don't need to worry about this event for our purposes. */ 20901 (void)pThis; 20902 (void)pDeviceID; 20903 return S_OK; 20904 } 20905 20906 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) 20907 { 20908 #ifdef MA_DEBUG_OUTPUT 20909 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ 20910 #endif 20911 20912 (void)role; 20913 20914 /* We only care about devices with the same data flow as the current device. */ 20915 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || 20916 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || 20917 (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { 20918 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); 20919 return S_OK; 20920 } 20921 20922 /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ 20923 if (pThis->pDevice->type == ma_device_type_loopback) { 20924 dataFlow = ma_eCapture; 20925 } 20926 20927 /* Don't do automatic stream routing if we're not allowed. */ 20928 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || 20929 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { 20930 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); 20931 return S_OK; 20932 } 20933 20934 /* 20935 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to 20936 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once 20937 it's fixed. 20938 */ 20939 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || 20940 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { 20941 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); 20942 return S_OK; 20943 } 20944 20945 20946 20947 /* 20948 Second attempt at device rerouting. We're going to retrieve the device's state at the time of 20949 the route change. We're then going to stop the device, reinitialize the device, and then start 20950 it again if the state before stopping was ma_device_state_started. 20951 */ 20952 { 20953 ma_uint32 previousState = ma_device_get_state(pThis->pDevice); 20954 ma_bool8 restartDevice = MA_FALSE; 20955 20956 if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { 20957 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); 20958 return S_OK; 20959 } 20960 20961 if (previousState == ma_device_state_started) { 20962 ma_device_stop(pThis->pDevice); 20963 restartDevice = MA_TRUE; 20964 } 20965 20966 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ 20967 ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); 20968 { 20969 if (dataFlow == ma_eRender) { 20970 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); 20971 20972 if (pThis->pDevice->wasapi.isDetachedPlayback) { 20973 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; 20974 20975 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { 20976 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ 20977 } 20978 else { 20979 restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ 20980 } 20981 } 20982 } 20983 else { 20984 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); 20985 20986 if (pThis->pDevice->wasapi.isDetachedCapture) { 20987 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; 20988 20989 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { 20990 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ 20991 } 20992 else { 20993 restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ 20994 } 20995 } 20996 } 20997 } 20998 ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); 20999 21000 if (restartDevice) { 21001 ma_device_start(pThis->pDevice); 21002 } 21003 } 21004 } 21005 21006 return S_OK; 21007 } 21008 21009 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) 21010 { 21011 #ifdef MA_DEBUG_OUTPUT 21012 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ 21013 #endif 21014 21015 (void)pThis; 21016 (void)pDeviceID; 21017 (void)key; 21018 return S_OK; 21019 } 21020 21021 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { 21022 ma_IMMNotificationClient_QueryInterface, 21023 ma_IMMNotificationClient_AddRef, 21024 ma_IMMNotificationClient_Release, 21025 ma_IMMNotificationClient_OnDeviceStateChanged, 21026 ma_IMMNotificationClient_OnDeviceAdded, 21027 ma_IMMNotificationClient_OnDeviceRemoved, 21028 ma_IMMNotificationClient_OnDefaultDeviceChanged, 21029 ma_IMMNotificationClient_OnPropertyValueChanged 21030 }; 21031 #endif /* MA_WIN32_DESKTOP */ 21032 21033 static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) 21034 { 21035 switch (usage) 21036 { 21037 case ma_wasapi_usage_default: return NULL; 21038 case ma_wasapi_usage_games: return "Games"; 21039 case ma_wasapi_usage_pro_audio: return "Pro Audio"; 21040 default: break; 21041 } 21042 21043 return NULL; 21044 } 21045 21046 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21047 typedef ma_IMMDevice ma_WASAPIDeviceInterface; 21048 #else 21049 typedef ma_IUnknown ma_WASAPIDeviceInterface; 21050 #endif 21051 21052 21053 #define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 21054 #define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 21055 #define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 21056 21057 static ma_context_command__wasapi ma_context_init_command__wasapi(int code) 21058 { 21059 ma_context_command__wasapi cmd; 21060 21061 MA_ZERO_OBJECT(&cmd); 21062 cmd.code = code; 21063 21064 return cmd; 21065 } 21066 21067 static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) 21068 { 21069 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ 21070 ma_result result; 21071 ma_bool32 isUsingLocalEvent = MA_FALSE; 21072 ma_event localEvent; 21073 21074 MA_ASSERT(pContext != NULL); 21075 MA_ASSERT(pCmd != NULL); 21076 21077 if (pCmd->pEvent == NULL) { 21078 isUsingLocalEvent = MA_TRUE; 21079 21080 result = ma_event_init(&localEvent); 21081 if (result != MA_SUCCESS) { 21082 return result; /* Failed to create the event for this command. */ 21083 } 21084 } 21085 21086 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ 21087 ma_mutex_lock(&pContext->wasapi.commandLock); 21088 { 21089 ma_uint32 index; 21090 21091 /* Spin until we've got some space available. */ 21092 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { 21093 ma_yield(); 21094 } 21095 21096 /* Space is now available. Can safely add to the list. */ 21097 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); 21098 pContext->wasapi.commands[index] = *pCmd; 21099 pContext->wasapi.commands[index].pEvent = &localEvent; 21100 pContext->wasapi.commandCount += 1; 21101 21102 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ 21103 ma_semaphore_release(&pContext->wasapi.commandSem); 21104 } 21105 ma_mutex_unlock(&pContext->wasapi.commandLock); 21106 21107 if (isUsingLocalEvent) { 21108 ma_event_wait(&localEvent); 21109 ma_event_uninit(&localEvent); 21110 } 21111 21112 return MA_SUCCESS; 21113 } 21114 21115 static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) 21116 { 21117 ma_result result = MA_SUCCESS; 21118 21119 MA_ASSERT(pContext != NULL); 21120 MA_ASSERT(pCmd != NULL); 21121 21122 result = ma_semaphore_wait(&pContext->wasapi.commandSem); 21123 if (result == MA_SUCCESS) { 21124 ma_mutex_lock(&pContext->wasapi.commandLock); 21125 { 21126 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; 21127 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); 21128 pContext->wasapi.commandCount -= 1; 21129 } 21130 ma_mutex_unlock(&pContext->wasapi.commandLock); 21131 } 21132 21133 return result; 21134 } 21135 21136 static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) 21137 { 21138 ma_result result; 21139 ma_context* pContext = (ma_context*)pUserData; 21140 MA_ASSERT(pContext != NULL); 21141 21142 for (;;) { 21143 ma_context_command__wasapi cmd; 21144 result = ma_context_next_command__wasapi(pContext, &cmd); 21145 if (result != MA_SUCCESS) { 21146 break; 21147 } 21148 21149 switch (cmd.code) 21150 { 21151 case MA_CONTEXT_COMMAND_QUIT__WASAPI: 21152 { 21153 /* Do nothing. Handled after the switch. */ 21154 } break; 21155 21156 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: 21157 { 21158 if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { 21159 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); 21160 } else { 21161 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); 21162 } 21163 } break; 21164 21165 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: 21166 { 21167 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { 21168 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { 21169 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); 21170 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; 21171 } 21172 } 21173 21174 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { 21175 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { 21176 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); 21177 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; 21178 } 21179 } 21180 } break; 21181 21182 default: 21183 { 21184 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ 21185 MA_ASSERT(MA_FALSE); 21186 } break; 21187 } 21188 21189 if (cmd.pEvent != NULL) { 21190 ma_event_signal(cmd.pEvent); 21191 } 21192 21193 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { 21194 break; /* Received a quit message. Get out of here. */ 21195 } 21196 } 21197 21198 return (ma_thread_result)0; 21199 } 21200 21201 static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) 21202 { 21203 ma_result result; 21204 ma_result cmdResult; 21205 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); 21206 cmd.data.createAudioClient.deviceType = deviceType; 21207 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; 21208 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; 21209 cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ 21210 21211 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ 21212 if (result != MA_SUCCESS) { 21213 return result; 21214 } 21215 21216 return *cmd.data.createAudioClient.pResult; 21217 } 21218 21219 #if 0 /* Not used at the moment, but leaving here for future use. */ 21220 static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) 21221 { 21222 ma_result result; 21223 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); 21224 cmd.data.releaseAudioClient.pDevice = pDevice; 21225 cmd.data.releaseAudioClient.deviceType = deviceType; 21226 21227 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ 21228 if (result != MA_SUCCESS) { 21229 return result; 21230 } 21231 21232 return MA_SUCCESS; 21233 } 21234 #endif 21235 21236 21237 static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) 21238 { 21239 MA_ASSERT(pWF != NULL); 21240 MA_ASSERT(pInfo != NULL); 21241 21242 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { 21243 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ 21244 } 21245 21246 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); 21247 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; 21248 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; 21249 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; 21250 pInfo->nativeDataFormatCount += 1; 21251 } 21252 21253 static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) 21254 { 21255 HRESULT hr; 21256 MA_WAVEFORMATEX* pWF = NULL; 21257 21258 MA_ASSERT(pAudioClient != NULL); 21259 MA_ASSERT(pInfo != NULL); 21260 21261 /* Shared Mode. We use GetMixFormat() here. */ 21262 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); 21263 if (SUCCEEDED(hr)) { 21264 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); 21265 } else { 21266 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); 21267 return ma_result_from_HRESULT(hr); 21268 } 21269 21270 /* 21271 Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on 21272 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on 21273 out, MA_SUCCESS is guaranteed to be returned. 21274 */ 21275 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21276 { 21277 ma_IPropertyStore *pProperties; 21278 21279 /* 21280 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is 21281 correct which will simplify our searching. 21282 */ 21283 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); 21284 if (SUCCEEDED(hr)) { 21285 MA_PROPVARIANT var; 21286 ma_PropVariantInit(&var); 21287 21288 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); 21289 if (SUCCEEDED(hr)) { 21290 pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; 21291 21292 /* 21293 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format 21294 first. If this fails, fall back to a search. 21295 */ 21296 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); 21297 if (SUCCEEDED(hr)) { 21298 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ 21299 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); 21300 } else { 21301 /* 21302 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel 21303 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. 21304 */ 21305 ma_uint32 channels = pWF->nChannels; 21306 ma_channel defaultChannelMap[MA_MAX_CHANNELS]; 21307 MA_WAVEFORMATEXTENSIBLE wf; 21308 ma_bool32 found; 21309 ma_uint32 iFormat; 21310 21311 /* Make sure we don't overflow the channel map. */ 21312 if (channels > MA_MAX_CHANNELS) { 21313 channels = MA_MAX_CHANNELS; 21314 } 21315 21316 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); 21317 21318 MA_ZERO_OBJECT(&wf); 21319 wf.cbSize = sizeof(wf); 21320 wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 21321 wf.nChannels = (WORD)channels; 21322 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); 21323 21324 found = MA_FALSE; 21325 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { 21326 ma_format format = g_maFormatPriorities[iFormat]; 21327 ma_uint32 iSampleRate; 21328 21329 wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); 21330 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); 21331 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; 21332 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; 21333 if (format == ma_format_f32) { 21334 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; 21335 } else { 21336 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; 21337 } 21338 21339 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { 21340 wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; 21341 21342 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); 21343 if (SUCCEEDED(hr)) { 21344 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); 21345 found = MA_TRUE; 21346 break; 21347 } 21348 } 21349 21350 if (found) { 21351 break; 21352 } 21353 } 21354 21355 ma_PropVariantClear(pContext, &var); 21356 21357 if (!found) { 21358 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); 21359 } 21360 } 21361 } else { 21362 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); 21363 } 21364 21365 ma_IPropertyStore_Release(pProperties); 21366 } else { 21367 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); 21368 } 21369 } 21370 #else 21371 { 21372 (void)pMMDevice; /* Unused. */ 21373 } 21374 #endif 21375 21376 return MA_SUCCESS; 21377 } 21378 21379 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21380 static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) 21381 { 21382 if (deviceType == ma_device_type_playback) { 21383 return ma_eRender; 21384 } else if (deviceType == ma_device_type_capture) { 21385 return ma_eCapture; 21386 } else { 21387 MA_ASSERT(MA_FALSE); 21388 return ma_eRender; /* Should never hit this. */ 21389 } 21390 } 21391 21392 static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) 21393 { 21394 HRESULT hr; 21395 ma_IMMDeviceEnumerator* pDeviceEnumerator; 21396 21397 MA_ASSERT(pContext != NULL); 21398 MA_ASSERT(ppDeviceEnumerator != NULL); 21399 21400 *ppDeviceEnumerator = NULL; /* Safety. */ 21401 21402 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); 21403 if (FAILED(hr)) { 21404 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); 21405 return ma_result_from_HRESULT(hr); 21406 } 21407 21408 *ppDeviceEnumerator = pDeviceEnumerator; 21409 21410 return MA_SUCCESS; 21411 } 21412 21413 static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) 21414 { 21415 HRESULT hr; 21416 ma_IMMDevice* pMMDefaultDevice = NULL; 21417 WCHAR* pDefaultDeviceID = NULL; 21418 ma_EDataFlow dataFlow; 21419 ma_ERole role; 21420 21421 MA_ASSERT(pContext != NULL); 21422 MA_ASSERT(pDeviceEnumerator != NULL); 21423 21424 (void)pContext; 21425 21426 /* Grab the EDataFlow type from the device type. */ 21427 dataFlow = ma_device_type_to_EDataFlow(deviceType); 21428 21429 /* The role is always eConsole, but we may make this configurable later. */ 21430 role = ma_eConsole; 21431 21432 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); 21433 if (FAILED(hr)) { 21434 return NULL; 21435 } 21436 21437 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); 21438 21439 ma_IMMDevice_Release(pMMDefaultDevice); 21440 pMMDefaultDevice = NULL; 21441 21442 if (FAILED(hr)) { 21443 return NULL; 21444 } 21445 21446 return pDefaultDeviceID; 21447 } 21448 21449 static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ 21450 { 21451 ma_result result; 21452 ma_IMMDeviceEnumerator* pDeviceEnumerator; 21453 WCHAR* pDefaultDeviceID = NULL; 21454 21455 MA_ASSERT(pContext != NULL); 21456 21457 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); 21458 if (result != MA_SUCCESS) { 21459 return NULL; 21460 } 21461 21462 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); 21463 21464 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); 21465 return pDefaultDeviceID; 21466 } 21467 21468 static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) 21469 { 21470 ma_IMMDeviceEnumerator* pDeviceEnumerator; 21471 HRESULT hr; 21472 21473 MA_ASSERT(pContext != NULL); 21474 MA_ASSERT(ppMMDevice != NULL); 21475 21476 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); 21477 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); 21478 ma_CoUninitialize(pContext); 21479 if (FAILED(hr)) { 21480 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); 21481 return ma_result_from_HRESULT(hr); 21482 } 21483 21484 if (pDeviceID == NULL) { 21485 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); 21486 } else { 21487 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); 21488 } 21489 21490 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); 21491 if (FAILED(hr)) { 21492 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); 21493 return ma_result_from_HRESULT(hr); 21494 } 21495 21496 return MA_SUCCESS; 21497 } 21498 21499 static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) 21500 { 21501 WCHAR* pDeviceIDString; 21502 HRESULT hr; 21503 21504 MA_ASSERT(pDeviceID != NULL); 21505 21506 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); 21507 if (SUCCEEDED(hr)) { 21508 size_t idlen = ma_strlen_WCHAR(pDeviceIDString); 21509 if (idlen+1 > ma_countof(pDeviceID->wasapi)) { 21510 ma_CoTaskMemFree(pContext, pDeviceIDString); 21511 MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ 21512 return MA_ERROR; 21513 } 21514 21515 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); 21516 pDeviceID->wasapi[idlen] = '\0'; 21517 21518 ma_CoTaskMemFree(pContext, pDeviceIDString); 21519 21520 return MA_SUCCESS; 21521 } 21522 21523 return MA_ERROR; 21524 } 21525 21526 static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) 21527 { 21528 ma_result result; 21529 HRESULT hr; 21530 21531 MA_ASSERT(pContext != NULL); 21532 MA_ASSERT(pMMDevice != NULL); 21533 MA_ASSERT(pInfo != NULL); 21534 21535 /* ID. */ 21536 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); 21537 if (result == MA_SUCCESS) { 21538 if (pDefaultDeviceID != NULL) { 21539 if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { 21540 pInfo->isDefault = MA_TRUE; 21541 } 21542 } 21543 } 21544 21545 /* Description / Friendly Name */ 21546 { 21547 ma_IPropertyStore *pProperties; 21548 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); 21549 if (SUCCEEDED(hr)) { 21550 MA_PROPVARIANT var; 21551 21552 ma_PropVariantInit(&var); 21553 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); 21554 if (SUCCEEDED(hr)) { 21555 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); 21556 ma_PropVariantClear(pContext, &var); 21557 } 21558 21559 ma_IPropertyStore_Release(pProperties); 21560 } 21561 } 21562 21563 /* Format */ 21564 if (!onlySimpleInfo) { 21565 ma_IAudioClient* pAudioClient; 21566 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); 21567 if (SUCCEEDED(hr)) { 21568 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); 21569 21570 ma_IAudioClient_Release(pAudioClient); 21571 return result; 21572 } else { 21573 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); 21574 return ma_result_from_HRESULT(hr); 21575 } 21576 } 21577 21578 return MA_SUCCESS; 21579 } 21580 21581 static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) 21582 { 21583 ma_result result = MA_SUCCESS; 21584 UINT deviceCount; 21585 HRESULT hr; 21586 ma_uint32 iDevice; 21587 WCHAR* pDefaultDeviceID = NULL; 21588 ma_IMMDeviceCollection* pDeviceCollection = NULL; 21589 21590 MA_ASSERT(pContext != NULL); 21591 MA_ASSERT(callback != NULL); 21592 21593 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ 21594 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); 21595 21596 /* We need to enumerate the devices which returns a device collection. */ 21597 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); 21598 if (SUCCEEDED(hr)) { 21599 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); 21600 if (FAILED(hr)) { 21601 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); 21602 result = ma_result_from_HRESULT(hr); 21603 goto done; 21604 } 21605 21606 for (iDevice = 0; iDevice < deviceCount; ++iDevice) { 21607 ma_device_info deviceInfo; 21608 ma_IMMDevice* pMMDevice; 21609 21610 MA_ZERO_OBJECT(&deviceInfo); 21611 21612 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); 21613 if (SUCCEEDED(hr)) { 21614 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ 21615 21616 ma_IMMDevice_Release(pMMDevice); 21617 if (result == MA_SUCCESS) { 21618 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); 21619 if (cbResult == MA_FALSE) { 21620 break; 21621 } 21622 } 21623 } 21624 } 21625 } 21626 21627 done: 21628 if (pDefaultDeviceID != NULL) { 21629 ma_CoTaskMemFree(pContext, pDefaultDeviceID); 21630 pDefaultDeviceID = NULL; 21631 } 21632 21633 if (pDeviceCollection != NULL) { 21634 ma_IMMDeviceCollection_Release(pDeviceCollection); 21635 pDeviceCollection = NULL; 21636 } 21637 21638 return result; 21639 } 21640 21641 static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) 21642 { 21643 ma_result result; 21644 HRESULT hr; 21645 21646 MA_ASSERT(pContext != NULL); 21647 MA_ASSERT(ppAudioClient != NULL); 21648 MA_ASSERT(ppMMDevice != NULL); 21649 21650 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); 21651 if (result != MA_SUCCESS) { 21652 return result; 21653 } 21654 21655 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); 21656 if (FAILED(hr)) { 21657 return ma_result_from_HRESULT(hr); 21658 } 21659 21660 return MA_SUCCESS; 21661 } 21662 #else 21663 static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) 21664 { 21665 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; 21666 ma_completion_handler_uwp completionHandler; 21667 IID iid; 21668 WCHAR* iidStr; 21669 HRESULT hr; 21670 ma_result result; 21671 HRESULT activateResult; 21672 ma_IUnknown* pActivatedInterface; 21673 21674 MA_ASSERT(pContext != NULL); 21675 MA_ASSERT(ppAudioClient != NULL); 21676 21677 if (pDeviceID != NULL) { 21678 iidStr = (WCHAR*)pDeviceID->wasapi; 21679 } else { 21680 if (deviceType == ma_device_type_capture) { 21681 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; 21682 } else { 21683 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; 21684 } 21685 21686 #if defined(__cplusplus) 21687 hr = StringFromIID(iid, &iidStr); 21688 #else 21689 hr = StringFromIID(&iid, &iidStr); 21690 #endif 21691 if (FAILED(hr)) { 21692 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); 21693 return ma_result_from_HRESULT(hr); 21694 } 21695 } 21696 21697 result = ma_completion_handler_uwp_init(&completionHandler); 21698 if (result != MA_SUCCESS) { 21699 ma_CoTaskMemFree(pContext, iidStr); 21700 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); 21701 return result; 21702 } 21703 21704 hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); 21705 if (FAILED(hr)) { 21706 ma_completion_handler_uwp_uninit(&completionHandler); 21707 ma_CoTaskMemFree(pContext, iidStr); 21708 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); 21709 return ma_result_from_HRESULT(hr); 21710 } 21711 21712 if (pDeviceID == NULL) { 21713 ma_CoTaskMemFree(pContext, iidStr); 21714 } 21715 21716 /* Wait for the async operation for finish. */ 21717 ma_completion_handler_uwp_wait(&completionHandler); 21718 ma_completion_handler_uwp_uninit(&completionHandler); 21719 21720 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); 21721 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); 21722 21723 if (FAILED(hr) || FAILED(activateResult)) { 21724 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); 21725 return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); 21726 } 21727 21728 /* Here is where we grab the IAudioClient interface. */ 21729 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); 21730 if (FAILED(hr)) { 21731 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); 21732 return ma_result_from_HRESULT(hr); 21733 } 21734 21735 if (ppActivatedInterface) { 21736 *ppActivatedInterface = pActivatedInterface; 21737 } else { 21738 ma_IUnknown_Release(pActivatedInterface); 21739 } 21740 21741 return MA_SUCCESS; 21742 } 21743 #endif 21744 21745 21746 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ 21747 typedef enum 21748 { 21749 MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, 21750 MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK 21751 } MA_AUDIOCLIENT_ACTIVATION_TYPE; 21752 21753 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ 21754 typedef enum 21755 { 21756 MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, 21757 MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE 21758 } MA_PROCESS_LOOPBACK_MODE; 21759 21760 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ 21761 typedef struct 21762 { 21763 DWORD TargetProcessId; 21764 MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; 21765 } MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; 21766 21767 #if defined(_MSC_VER) && !defined(__clang__) 21768 #pragma warning(push) 21769 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ 21770 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 21771 #pragma GCC diagnostic push 21772 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ 21773 #if defined(__clang__) 21774 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ 21775 #endif 21776 #endif 21777 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ 21778 typedef struct 21779 { 21780 MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; 21781 union 21782 { 21783 MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; 21784 }; 21785 } MA_AUDIOCLIENT_ACTIVATION_PARAMS; 21786 #if defined(_MSC_VER) && !defined(__clang__) 21787 #pragma warning(pop) 21788 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 21789 #pragma GCC diagnostic pop 21790 #endif 21791 21792 #define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" 21793 21794 static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) 21795 { 21796 ma_result result; 21797 ma_bool32 usingProcessLoopback = MA_FALSE; 21798 MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; 21799 MA_PROPVARIANT activationParams; 21800 MA_PROPVARIANT* pActivationParams = NULL; 21801 ma_device_id virtualDeviceID; 21802 21803 /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ 21804 if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { 21805 usingProcessLoopback = MA_TRUE; 21806 } 21807 21808 if (usingProcessLoopback) { 21809 MA_ZERO_OBJECT(&audioclientActivationParams); 21810 audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; 21811 audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; 21812 audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; 21813 21814 ma_PropVariantInit(&activationParams); 21815 activationParams.vt = MA_VT_BLOB; 21816 activationParams.blob.cbSize = sizeof(audioclientActivationParams); 21817 activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; 21818 pActivationParams = &activationParams; 21819 21820 /* When requesting a specific device ID we need to use a special device ID. */ 21821 MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ 21822 pDeviceID = &virtualDeviceID; 21823 } else { 21824 pActivationParams = NULL; /* No activation parameters required. */ 21825 } 21826 21827 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21828 result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); 21829 #else 21830 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); 21831 #endif 21832 21833 /* 21834 If loopback mode was requested with a process ID and initialization failed, it could be because it's 21835 trying to run on an older version of Windows where it's not supported. We need to let the caller 21836 know about this with a log message. 21837 */ 21838 if (result != MA_SUCCESS) { 21839 if (usingProcessLoopback) { 21840 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); 21841 } 21842 } 21843 21844 return result; 21845 } 21846 21847 21848 static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 21849 { 21850 /* Different enumeration for desktop and UWP. */ 21851 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21852 /* Desktop */ 21853 HRESULT hr; 21854 ma_IMMDeviceEnumerator* pDeviceEnumerator; 21855 21856 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); 21857 if (FAILED(hr)) { 21858 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); 21859 return ma_result_from_HRESULT(hr); 21860 } 21861 21862 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); 21863 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); 21864 21865 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); 21866 #else 21867 /* 21868 UWP 21869 21870 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate 21871 over devices without using MMDevice, I'm restricting devices to defaults. 21872 21873 Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ 21874 */ 21875 if (callback) { 21876 ma_bool32 cbResult = MA_TRUE; 21877 21878 /* Playback. */ 21879 if (cbResult) { 21880 ma_device_info deviceInfo; 21881 MA_ZERO_OBJECT(&deviceInfo); 21882 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 21883 deviceInfo.isDefault = MA_TRUE; 21884 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 21885 } 21886 21887 /* Capture. */ 21888 if (cbResult) { 21889 ma_device_info deviceInfo; 21890 MA_ZERO_OBJECT(&deviceInfo); 21891 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 21892 deviceInfo.isDefault = MA_TRUE; 21893 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 21894 } 21895 } 21896 #endif 21897 21898 return MA_SUCCESS; 21899 } 21900 21901 static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 21902 { 21903 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21904 ma_result result; 21905 ma_IMMDevice* pMMDevice = NULL; 21906 WCHAR* pDefaultDeviceID = NULL; 21907 21908 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); 21909 if (result != MA_SUCCESS) { 21910 return result; 21911 } 21912 21913 /* We need the default device ID so we can set the isDefault flag in the device info. */ 21914 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); 21915 21916 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ 21917 21918 if (pDefaultDeviceID != NULL) { 21919 ma_CoTaskMemFree(pContext, pDefaultDeviceID); 21920 pDefaultDeviceID = NULL; 21921 } 21922 21923 ma_IMMDevice_Release(pMMDevice); 21924 21925 return result; 21926 #else 21927 ma_IAudioClient* pAudioClient; 21928 ma_result result; 21929 21930 /* UWP currently only uses default devices. */ 21931 if (deviceType == ma_device_type_playback) { 21932 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 21933 } else { 21934 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 21935 } 21936 21937 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); 21938 if (result != MA_SUCCESS) { 21939 return result; 21940 } 21941 21942 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); 21943 21944 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ 21945 21946 ma_IAudioClient_Release(pAudioClient); 21947 return result; 21948 #endif 21949 } 21950 21951 static ma_result ma_device_uninit__wasapi(ma_device* pDevice) 21952 { 21953 MA_ASSERT(pDevice != NULL); 21954 21955 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 21956 if (pDevice->wasapi.pDeviceEnumerator) { 21957 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); 21958 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); 21959 } 21960 #endif 21961 21962 if (pDevice->wasapi.pRenderClient) { 21963 if (pDevice->wasapi.pMappedBufferPlayback != NULL) { 21964 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); 21965 pDevice->wasapi.pMappedBufferPlayback = NULL; 21966 pDevice->wasapi.mappedBufferPlaybackCap = 0; 21967 pDevice->wasapi.mappedBufferPlaybackLen = 0; 21968 } 21969 21970 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); 21971 } 21972 if (pDevice->wasapi.pCaptureClient) { 21973 if (pDevice->wasapi.pMappedBufferCapture != NULL) { 21974 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); 21975 pDevice->wasapi.pMappedBufferCapture = NULL; 21976 pDevice->wasapi.mappedBufferCaptureCap = 0; 21977 pDevice->wasapi.mappedBufferCaptureLen = 0; 21978 } 21979 21980 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); 21981 } 21982 21983 if (pDevice->wasapi.pAudioClientPlayback) { 21984 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); 21985 } 21986 if (pDevice->wasapi.pAudioClientCapture) { 21987 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 21988 } 21989 21990 if (pDevice->wasapi.hEventPlayback) { 21991 CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); 21992 } 21993 if (pDevice->wasapi.hEventCapture) { 21994 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); 21995 } 21996 21997 return MA_SUCCESS; 21998 } 21999 22000 22001 typedef struct 22002 { 22003 /* Input. */ 22004 ma_format formatIn; 22005 ma_uint32 channelsIn; 22006 ma_uint32 sampleRateIn; 22007 ma_channel channelMapIn[MA_MAX_CHANNELS]; 22008 ma_uint32 periodSizeInFramesIn; 22009 ma_uint32 periodSizeInMillisecondsIn; 22010 ma_uint32 periodsIn; 22011 ma_share_mode shareMode; 22012 ma_performance_profile performanceProfile; 22013 ma_bool32 noAutoConvertSRC; 22014 ma_bool32 noDefaultQualitySRC; 22015 ma_bool32 noHardwareOffloading; 22016 ma_uint32 loopbackProcessID; 22017 ma_bool32 loopbackProcessExclude; 22018 22019 /* Output. */ 22020 ma_IAudioClient* pAudioClient; 22021 ma_IAudioRenderClient* pRenderClient; 22022 ma_IAudioCaptureClient* pCaptureClient; 22023 ma_format formatOut; 22024 ma_uint32 channelsOut; 22025 ma_uint32 sampleRateOut; 22026 ma_channel channelMapOut[MA_MAX_CHANNELS]; 22027 ma_uint32 periodSizeInFramesOut; 22028 ma_uint32 periodsOut; 22029 ma_bool32 usingAudioClient3; 22030 char deviceName[256]; 22031 ma_device_id id; 22032 } ma_device_init_internal_data__wasapi; 22033 22034 static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) 22035 { 22036 HRESULT hr; 22037 ma_result result = MA_SUCCESS; 22038 const char* errorMsg = ""; 22039 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; 22040 DWORD streamFlags = 0; 22041 MA_REFERENCE_TIME periodDurationInMicroseconds; 22042 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; 22043 MA_WAVEFORMATEXTENSIBLE wf; 22044 ma_WASAPIDeviceInterface* pDeviceInterface = NULL; 22045 ma_IAudioClient2* pAudioClient2; 22046 ma_uint32 nativeSampleRate; 22047 ma_bool32 usingProcessLoopback = MA_FALSE; 22048 22049 MA_ASSERT(pContext != NULL); 22050 MA_ASSERT(pData != NULL); 22051 22052 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ 22053 if (deviceType == ma_device_type_duplex) { 22054 return MA_INVALID_ARGS; 22055 } 22056 22057 usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; 22058 22059 pData->pAudioClient = NULL; 22060 pData->pRenderClient = NULL; 22061 pData->pCaptureClient = NULL; 22062 22063 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; 22064 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ 22065 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; 22066 } 22067 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { 22068 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; 22069 } 22070 if (deviceType == ma_device_type_loopback) { 22071 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; 22072 } 22073 22074 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); 22075 if (result != MA_SUCCESS) { 22076 goto done; 22077 } 22078 22079 MA_ZERO_OBJECT(&wf); 22080 22081 /* Try enabling hardware offloading. */ 22082 if (!pData->noHardwareOffloading) { 22083 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); 22084 if (SUCCEEDED(hr)) { 22085 BOOL isHardwareOffloadingSupported = 0; 22086 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); 22087 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { 22088 ma_AudioClientProperties clientProperties; 22089 MA_ZERO_OBJECT(&clientProperties); 22090 clientProperties.cbSize = sizeof(clientProperties); 22091 clientProperties.bIsOffload = 1; 22092 clientProperties.eCategory = MA_AudioCategory_Other; 22093 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); 22094 } 22095 22096 pAudioClient2->lpVtbl->Release(pAudioClient2); 22097 } 22098 } 22099 22100 /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ 22101 result = MA_FORMAT_NOT_SUPPORTED; 22102 if (pData->shareMode == ma_share_mode_exclusive) { 22103 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22104 /* In exclusive mode on desktop we always use the backend's native format. */ 22105 ma_IPropertyStore* pStore = NULL; 22106 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); 22107 if (SUCCEEDED(hr)) { 22108 MA_PROPVARIANT prop; 22109 ma_PropVariantInit(&prop); 22110 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); 22111 if (SUCCEEDED(hr)) { 22112 MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; 22113 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); 22114 if (SUCCEEDED(hr)) { 22115 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); 22116 } 22117 22118 ma_PropVariantClear(pContext, &prop); 22119 } 22120 22121 ma_IPropertyStore_Release(pStore); 22122 } 22123 #else 22124 /* 22125 I do not know how to query the device's native format on UWP so for now I'm just disabling support for 22126 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() 22127 until you find one that works. 22128 22129 TODO: Add support for exclusive mode to UWP. 22130 */ 22131 hr = S_FALSE; 22132 #endif 22133 22134 if (hr == S_OK) { 22135 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; 22136 result = MA_SUCCESS; 22137 } else { 22138 result = MA_SHARE_MODE_NOT_SUPPORTED; 22139 } 22140 } else { 22141 /* In shared mode we are always using the format reported by the operating system. */ 22142 MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; 22143 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); 22144 if (hr != S_OK) { 22145 /* When using process-specific loopback, GetMixFormat() seems to always fail. */ 22146 if (usingProcessLoopback) { 22147 wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; 22148 wf.nChannels = 2; 22149 wf.nSamplesPerSec = 44100; 22150 wf.wBitsPerSample = 32; 22151 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; 22152 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; 22153 wf.cbSize = sizeof(MA_WAVEFORMATEX); 22154 22155 result = MA_SUCCESS; 22156 } else { 22157 result = MA_FORMAT_NOT_SUPPORTED; 22158 } 22159 } else { 22160 /* 22161 I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself 22162 is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE 22163 want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be 22164 safe and only copy the WAVEFORMATEX part. 22165 */ 22166 if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { 22167 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); 22168 } else { 22169 /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ 22170 size_t cbSize = pNativeFormat->cbSize; 22171 if (cbSize == 0) { 22172 cbSize = sizeof(MA_WAVEFORMATEX); 22173 } 22174 22175 /* Make sure we don't copy more than the capacity of `wf`. */ 22176 if (cbSize > sizeof(wf)) { 22177 cbSize = sizeof(wf); 22178 } 22179 22180 MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); 22181 } 22182 22183 result = MA_SUCCESS; 22184 } 22185 22186 ma_CoTaskMemFree(pContext, pNativeFormat); 22187 22188 shareMode = MA_AUDCLNT_SHAREMODE_SHARED; 22189 } 22190 22191 /* Return an error if we still haven't found a format. */ 22192 if (result != MA_SUCCESS) { 22193 errorMsg = "[WASAPI] Failed to find best device mix format."; 22194 goto done; 22195 } 22196 22197 /* 22198 Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use 22199 WASAPI to perform the sample rate conversion. 22200 */ 22201 nativeSampleRate = wf.nSamplesPerSec; 22202 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { 22203 wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; 22204 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; 22205 } 22206 22207 pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); 22208 if (pData->formatOut == ma_format_unknown) { 22209 /* 22210 The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED 22211 in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for 22212 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. 22213 */ 22214 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { 22215 result = MA_SHARE_MODE_NOT_SUPPORTED; 22216 } else { 22217 result = MA_FORMAT_NOT_SUPPORTED; 22218 } 22219 22220 errorMsg = "[WASAPI] Native format not supported."; 22221 goto done; 22222 } 22223 22224 pData->channelsOut = wf.nChannels; 22225 pData->sampleRateOut = wf.nSamplesPerSec; 22226 22227 /* 22228 Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns 22229 a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this 22230 case we'll just use the default channel map. 22231 */ 22232 if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { 22233 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); 22234 } else { 22235 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); 22236 } 22237 22238 /* Period size. */ 22239 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; 22240 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; 22241 if (pData->periodSizeInFramesOut == 0) { 22242 if (pData->periodSizeInMillisecondsIn == 0) { 22243 if (pData->performanceProfile == ma_performance_profile_low_latency) { 22244 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); 22245 } else { 22246 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); 22247 } 22248 } else { 22249 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); 22250 } 22251 } 22252 22253 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; 22254 22255 22256 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ 22257 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { 22258 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; 22259 22260 /* 22261 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing 22262 it and trying it again. 22263 */ 22264 hr = E_FAIL; 22265 for (;;) { 22266 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); 22267 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { 22268 if (bufferDuration > 500*10000) { 22269 break; 22270 } else { 22271 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ 22272 break; 22273 } 22274 22275 bufferDuration = bufferDuration * 2; 22276 continue; 22277 } 22278 } else { 22279 break; 22280 } 22281 } 22282 22283 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { 22284 ma_uint32 bufferSizeInFrames; 22285 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); 22286 if (SUCCEEDED(hr)) { 22287 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); 22288 22289 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ 22290 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); 22291 22292 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22293 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); 22294 #else 22295 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); 22296 #endif 22297 22298 if (SUCCEEDED(hr)) { 22299 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); 22300 } 22301 } 22302 } 22303 22304 if (FAILED(hr)) { 22305 /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ 22306 if (hr == E_ACCESSDENIED) { 22307 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; 22308 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { 22309 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; 22310 } else { 22311 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); 22312 } 22313 goto done; 22314 } 22315 } 22316 22317 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { 22318 /* 22319 Low latency shared mode via IAudioClient3. 22320 22321 NOTE 22322 ==== 22323 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the 22324 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using 22325 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to 22326 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. 22327 */ 22328 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE 22329 { 22330 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { 22331 ma_IAudioClient3* pAudioClient3 = NULL; 22332 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); 22333 if (SUCCEEDED(hr)) { 22334 ma_uint32 defaultPeriodInFrames; 22335 ma_uint32 fundamentalPeriodInFrames; 22336 ma_uint32 minPeriodInFrames; 22337 ma_uint32 maxPeriodInFrames; 22338 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); 22339 if (SUCCEEDED(hr)) { 22340 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; 22341 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; 22342 22343 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ 22344 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; 22345 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; 22346 22347 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ 22348 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); 22349 22350 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); 22351 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); 22352 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); 22353 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); 22354 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); 22355 22356 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ 22357 if (actualPeriodInFrames >= desiredPeriodInFrames) { 22358 /* 22359 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, 22360 IAudioClient3_InitializeSharedAudioStream() will fail. 22361 */ 22362 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); 22363 if (SUCCEEDED(hr)) { 22364 wasInitializedUsingIAudioClient3 = MA_TRUE; 22365 pData->periodSizeInFramesOut = actualPeriodInFrames; 22366 22367 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); 22368 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); 22369 } else { 22370 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); 22371 } 22372 } else { 22373 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); 22374 } 22375 } else { 22376 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); 22377 } 22378 22379 ma_IAudioClient3_Release(pAudioClient3); 22380 pAudioClient3 = NULL; 22381 } 22382 } 22383 } 22384 #else 22385 { 22386 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); 22387 } 22388 #endif 22389 22390 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ 22391 if (!wasInitializedUsingIAudioClient3) { 22392 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ 22393 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); 22394 if (FAILED(hr)) { 22395 if (hr == E_ACCESSDENIED) { 22396 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; 22397 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { 22398 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; 22399 } else { 22400 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); 22401 } 22402 22403 goto done; 22404 } 22405 } 22406 } 22407 22408 if (!wasInitializedUsingIAudioClient3) { 22409 ma_uint32 bufferSizeInFrames = 0; 22410 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); 22411 if (FAILED(hr)) { 22412 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); 22413 goto done; 22414 } 22415 22416 /* 22417 When using process loopback mode, retrieval of the buffer size seems to result in totally 22418 incorrect values. In this case we'll just assume it's the same size as what we requested 22419 when we initialized the client. 22420 */ 22421 if (usingProcessLoopback) { 22422 bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); 22423 } 22424 22425 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; 22426 } 22427 22428 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; 22429 22430 22431 if (deviceType == ma_device_type_playback) { 22432 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); 22433 } else { 22434 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); 22435 } 22436 22437 /*if (FAILED(hr)) {*/ 22438 if (result != MA_SUCCESS) { 22439 errorMsg = "[WASAPI] Failed to get audio client service."; 22440 goto done; 22441 } 22442 22443 22444 /* Grab the name of the device. */ 22445 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22446 { 22447 ma_IPropertyStore *pProperties; 22448 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); 22449 if (SUCCEEDED(hr)) { 22450 MA_PROPVARIANT varName; 22451 ma_PropVariantInit(&varName); 22452 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); 22453 if (SUCCEEDED(hr)) { 22454 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); 22455 ma_PropVariantClear(pContext, &varName); 22456 } 22457 22458 ma_IPropertyStore_Release(pProperties); 22459 } 22460 } 22461 #endif 22462 22463 /* 22464 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic 22465 stream routing so that IDs can be compared and we can determine which device has been detached 22466 and whether or not it matches with our ma_device. 22467 */ 22468 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22469 { 22470 /* Desktop */ 22471 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); 22472 } 22473 #else 22474 { 22475 /* UWP */ 22476 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ 22477 } 22478 #endif 22479 22480 done: 22481 /* Clean up. */ 22482 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22483 if (pDeviceInterface != NULL) { 22484 ma_IMMDevice_Release(pDeviceInterface); 22485 } 22486 #else 22487 if (pDeviceInterface != NULL) { 22488 ma_IUnknown_Release(pDeviceInterface); 22489 } 22490 #endif 22491 22492 if (result != MA_SUCCESS) { 22493 if (pData->pRenderClient) { 22494 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); 22495 pData->pRenderClient = NULL; 22496 } 22497 if (pData->pCaptureClient) { 22498 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); 22499 pData->pCaptureClient = NULL; 22500 } 22501 if (pData->pAudioClient) { 22502 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); 22503 pData->pAudioClient = NULL; 22504 } 22505 22506 if (errorMsg != NULL && errorMsg[0] != '\0') { 22507 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); 22508 } 22509 22510 return result; 22511 } else { 22512 return MA_SUCCESS; 22513 } 22514 } 22515 22516 static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) 22517 { 22518 ma_device_init_internal_data__wasapi data; 22519 ma_result result; 22520 22521 MA_ASSERT(pDevice != NULL); 22522 22523 /* We only re-initialize the playback or capture device. Never a full-duplex device. */ 22524 if (deviceType == ma_device_type_duplex) { 22525 return MA_INVALID_ARGS; 22526 } 22527 22528 22529 /* 22530 Before reinitializing the device we need to free the previous audio clients. 22531 22532 There's a known memory leak here. We will be calling this from the routing change callback that 22533 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion 22534 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably 22535 need some system where we post an event, but delay the execution of it until the callback has 22536 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for 22537 a command thread which might be useful for this. 22538 */ 22539 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { 22540 if (pDevice->wasapi.pCaptureClient) { 22541 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); 22542 pDevice->wasapi.pCaptureClient = NULL; 22543 } 22544 22545 if (pDevice->wasapi.pAudioClientCapture) { 22546 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ 22547 pDevice->wasapi.pAudioClientCapture = NULL; 22548 } 22549 } 22550 22551 if (deviceType == ma_device_type_playback) { 22552 if (pDevice->wasapi.pRenderClient) { 22553 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); 22554 pDevice->wasapi.pRenderClient = NULL; 22555 } 22556 22557 if (pDevice->wasapi.pAudioClientPlayback) { 22558 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ 22559 pDevice->wasapi.pAudioClientPlayback = NULL; 22560 } 22561 } 22562 22563 22564 if (deviceType == ma_device_type_playback) { 22565 data.formatIn = pDevice->playback.format; 22566 data.channelsIn = pDevice->playback.channels; 22567 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); 22568 data.shareMode = pDevice->playback.shareMode; 22569 } else { 22570 data.formatIn = pDevice->capture.format; 22571 data.channelsIn = pDevice->capture.channels; 22572 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); 22573 data.shareMode = pDevice->capture.shareMode; 22574 } 22575 22576 data.sampleRateIn = pDevice->sampleRate; 22577 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; 22578 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; 22579 data.periodsIn = pDevice->wasapi.originalPeriods; 22580 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; 22581 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; 22582 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; 22583 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; 22584 data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; 22585 data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; 22586 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); 22587 if (result != MA_SUCCESS) { 22588 return result; 22589 } 22590 22591 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ 22592 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { 22593 pDevice->wasapi.pAudioClientCapture = data.pAudioClient; 22594 pDevice->wasapi.pCaptureClient = data.pCaptureClient; 22595 22596 pDevice->capture.internalFormat = data.formatOut; 22597 pDevice->capture.internalChannels = data.channelsOut; 22598 pDevice->capture.internalSampleRate = data.sampleRateOut; 22599 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); 22600 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; 22601 pDevice->capture.internalPeriods = data.periodsOut; 22602 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); 22603 22604 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); 22605 22606 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; 22607 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); 22608 22609 /* We must always have a valid ID. */ 22610 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); 22611 } 22612 22613 if (deviceType == ma_device_type_playback) { 22614 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; 22615 pDevice->wasapi.pRenderClient = data.pRenderClient; 22616 22617 pDevice->playback.internalFormat = data.formatOut; 22618 pDevice->playback.internalChannels = data.channelsOut; 22619 pDevice->playback.internalSampleRate = data.sampleRateOut; 22620 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); 22621 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; 22622 pDevice->playback.internalPeriods = data.periodsOut; 22623 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); 22624 22625 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); 22626 22627 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; 22628 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); 22629 22630 /* We must always have a valid ID because rerouting will look at it. */ 22631 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); 22632 } 22633 22634 return MA_SUCCESS; 22635 } 22636 22637 static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 22638 { 22639 ma_result result = MA_SUCCESS; 22640 22641 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22642 HRESULT hr; 22643 ma_IMMDeviceEnumerator* pDeviceEnumerator; 22644 #endif 22645 22646 MA_ASSERT(pDevice != NULL); 22647 22648 MA_ZERO_OBJECT(&pDevice->wasapi); 22649 pDevice->wasapi.usage = pConfig->wasapi.usage; 22650 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; 22651 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; 22652 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; 22653 pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; 22654 pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; 22655 22656 /* Exclusive mode is not allowed with loopback. */ 22657 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { 22658 return MA_INVALID_DEVICE_CONFIG; 22659 } 22660 22661 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { 22662 ma_device_init_internal_data__wasapi data; 22663 data.formatIn = pDescriptorCapture->format; 22664 data.channelsIn = pDescriptorCapture->channels; 22665 data.sampleRateIn = pDescriptorCapture->sampleRate; 22666 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); 22667 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; 22668 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; 22669 data.periodsIn = pDescriptorCapture->periodCount; 22670 data.shareMode = pDescriptorCapture->shareMode; 22671 data.performanceProfile = pConfig->performanceProfile; 22672 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; 22673 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; 22674 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; 22675 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; 22676 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; 22677 22678 result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); 22679 if (result != MA_SUCCESS) { 22680 return result; 22681 } 22682 22683 pDevice->wasapi.pAudioClientCapture = data.pAudioClient; 22684 pDevice->wasapi.pCaptureClient = data.pCaptureClient; 22685 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; 22686 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; 22687 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; 22688 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; 22689 22690 /* 22691 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, 22692 however, because we want to block until we actually have something for the first call to ma_device_read(). 22693 */ 22694 pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ 22695 if (pDevice->wasapi.hEventCapture == NULL) { 22696 result = ma_result_from_GetLastError(GetLastError()); 22697 22698 if (pDevice->wasapi.pCaptureClient != NULL) { 22699 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); 22700 pDevice->wasapi.pCaptureClient = NULL; 22701 } 22702 if (pDevice->wasapi.pAudioClientCapture != NULL) { 22703 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 22704 pDevice->wasapi.pAudioClientCapture = NULL; 22705 } 22706 22707 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); 22708 return result; 22709 } 22710 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); 22711 22712 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; 22713 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); 22714 22715 /* We must always have a valid ID. */ 22716 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); 22717 22718 /* The descriptor needs to be updated with actual values. */ 22719 pDescriptorCapture->format = data.formatOut; 22720 pDescriptorCapture->channels = data.channelsOut; 22721 pDescriptorCapture->sampleRate = data.sampleRateOut; 22722 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); 22723 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; 22724 pDescriptorCapture->periodCount = data.periodsOut; 22725 } 22726 22727 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 22728 ma_device_init_internal_data__wasapi data; 22729 data.formatIn = pDescriptorPlayback->format; 22730 data.channelsIn = pDescriptorPlayback->channels; 22731 data.sampleRateIn = pDescriptorPlayback->sampleRate; 22732 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); 22733 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; 22734 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; 22735 data.periodsIn = pDescriptorPlayback->periodCount; 22736 data.shareMode = pDescriptorPlayback->shareMode; 22737 data.performanceProfile = pConfig->performanceProfile; 22738 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; 22739 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; 22740 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; 22741 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; 22742 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; 22743 22744 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); 22745 if (result != MA_SUCCESS) { 22746 if (pConfig->deviceType == ma_device_type_duplex) { 22747 if (pDevice->wasapi.pCaptureClient != NULL) { 22748 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); 22749 pDevice->wasapi.pCaptureClient = NULL; 22750 } 22751 if (pDevice->wasapi.pAudioClientCapture != NULL) { 22752 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 22753 pDevice->wasapi.pAudioClientCapture = NULL; 22754 } 22755 22756 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); 22757 pDevice->wasapi.hEventCapture = NULL; 22758 } 22759 return result; 22760 } 22761 22762 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; 22763 pDevice->wasapi.pRenderClient = data.pRenderClient; 22764 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; 22765 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; 22766 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; 22767 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; 22768 22769 /* 22770 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled 22771 only after the whole available space has been filled, never before. 22772 22773 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able 22774 to get passed WaitForMultipleObjects(). 22775 */ 22776 pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ 22777 if (pDevice->wasapi.hEventPlayback == NULL) { 22778 result = ma_result_from_GetLastError(GetLastError()); 22779 22780 if (pConfig->deviceType == ma_device_type_duplex) { 22781 if (pDevice->wasapi.pCaptureClient != NULL) { 22782 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); 22783 pDevice->wasapi.pCaptureClient = NULL; 22784 } 22785 if (pDevice->wasapi.pAudioClientCapture != NULL) { 22786 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 22787 pDevice->wasapi.pAudioClientCapture = NULL; 22788 } 22789 22790 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); 22791 pDevice->wasapi.hEventCapture = NULL; 22792 } 22793 22794 if (pDevice->wasapi.pRenderClient != NULL) { 22795 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); 22796 pDevice->wasapi.pRenderClient = NULL; 22797 } 22798 if (pDevice->wasapi.pAudioClientPlayback != NULL) { 22799 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); 22800 pDevice->wasapi.pAudioClientPlayback = NULL; 22801 } 22802 22803 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); 22804 return result; 22805 } 22806 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); 22807 22808 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; 22809 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); 22810 22811 /* We must always have a valid ID because rerouting will look at it. */ 22812 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); 22813 22814 /* The descriptor needs to be updated with actual values. */ 22815 pDescriptorPlayback->format = data.formatOut; 22816 pDescriptorPlayback->channels = data.channelsOut; 22817 pDescriptorPlayback->sampleRate = data.sampleRateOut; 22818 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); 22819 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; 22820 pDescriptorPlayback->periodCount = data.periodsOut; 22821 } 22822 22823 /* 22824 We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When 22825 we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just 22826 stop the device outright and let the application handle it. 22827 */ 22828 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 22829 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { 22830 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { 22831 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; 22832 } 22833 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { 22834 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; 22835 } 22836 } 22837 22838 ma_mutex_init(&pDevice->wasapi.rerouteLock); 22839 22840 hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); 22841 if (FAILED(hr)) { 22842 ma_device_uninit__wasapi(pDevice); 22843 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); 22844 return ma_result_from_HRESULT(hr); 22845 } 22846 22847 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; 22848 pDevice->wasapi.notificationClient.counter = 1; 22849 pDevice->wasapi.notificationClient.pDevice = pDevice; 22850 22851 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); 22852 if (SUCCEEDED(hr)) { 22853 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; 22854 } else { 22855 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ 22856 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); 22857 } 22858 #endif 22859 22860 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); 22861 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); 22862 22863 return MA_SUCCESS; 22864 } 22865 22866 static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) 22867 { 22868 ma_uint32 paddingFramesCount; 22869 HRESULT hr; 22870 ma_share_mode shareMode; 22871 22872 MA_ASSERT(pDevice != NULL); 22873 MA_ASSERT(pFrameCount != NULL); 22874 22875 *pFrameCount = 0; 22876 22877 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { 22878 return MA_INVALID_OPERATION; 22879 } 22880 22881 /* 22882 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing 22883 higher level function calls from doing anything because it thinks nothing is available. I have 22884 taken a look at the documentation and it looks like this is unnecessary in exclusive mode. 22885 22886 From Microsoft's documentation: 22887 22888 For an exclusive-mode rendering or capture stream that was initialized with the 22889 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding 22890 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during 22891 each processing pass. 22892 22893 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the 22894 entire buffer. This depends on the caller making sure they wait on the event handler. 22895 */ 22896 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; 22897 if (shareMode == ma_share_mode_shared) { 22898 /* Shared mode. */ 22899 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); 22900 if (FAILED(hr)) { 22901 return ma_result_from_HRESULT(hr); 22902 } 22903 22904 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { 22905 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; 22906 } else { 22907 *pFrameCount = paddingFramesCount; 22908 } 22909 } else { 22910 /* Exclusive mode. */ 22911 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { 22912 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; 22913 } else { 22914 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; 22915 } 22916 } 22917 22918 return MA_SUCCESS; 22919 } 22920 22921 22922 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) 22923 { 22924 ma_result result; 22925 22926 if (deviceType == ma_device_type_duplex) { 22927 return MA_INVALID_ARGS; 22928 } 22929 22930 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); 22931 22932 result = ma_device_reinit__wasapi(pDevice, deviceType); 22933 if (result != MA_SUCCESS) { 22934 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); 22935 return result; 22936 } 22937 22938 ma_device__post_init_setup(pDevice, deviceType); 22939 ma_device__on_notification_rerouted(pDevice); 22940 22941 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); 22942 22943 return MA_SUCCESS; 22944 } 22945 22946 static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) 22947 { 22948 HRESULT hr; 22949 22950 if (pDevice->pContext->wasapi.hAvrt) { 22951 const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); 22952 if (pTaskName) { 22953 DWORD idx = 0; 22954 pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); 22955 } 22956 } 22957 22958 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 22959 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 22960 if (FAILED(hr)) { 22961 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); 22962 return ma_result_from_HRESULT(hr); 22963 } 22964 22965 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); 22966 } 22967 22968 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 22969 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); 22970 if (FAILED(hr)) { 22971 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); 22972 return ma_result_from_HRESULT(hr); 22973 } 22974 22975 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); 22976 } 22977 22978 return MA_SUCCESS; 22979 } 22980 22981 static ma_result ma_device_start__wasapi(ma_device* pDevice) 22982 { 22983 ma_result result; 22984 22985 MA_ASSERT(pDevice != NULL); 22986 22987 /* Wait for any rerouting to finish before attempting to start the device. */ 22988 ma_mutex_lock(&pDevice->wasapi.rerouteLock); 22989 { 22990 result = ma_device_start__wasapi_nolock(pDevice); 22991 } 22992 ma_mutex_unlock(&pDevice->wasapi.rerouteLock); 22993 22994 return result; 22995 } 22996 22997 static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) 22998 { 22999 ma_result result; 23000 HRESULT hr; 23001 23002 MA_ASSERT(pDevice != NULL); 23003 23004 if (pDevice->wasapi.hAvrtHandle) { 23005 ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); 23006 pDevice->wasapi.hAvrtHandle = NULL; 23007 } 23008 23009 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 23010 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 23011 if (FAILED(hr)) { 23012 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); 23013 return ma_result_from_HRESULT(hr); 23014 } 23015 23016 /* The audio client needs to be reset otherwise restarting will fail. */ 23017 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); 23018 if (FAILED(hr)) { 23019 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); 23020 return ma_result_from_HRESULT(hr); 23021 } 23022 23023 /* If we have a mapped buffer we need to release it. */ 23024 if (pDevice->wasapi.pMappedBufferCapture != NULL) { 23025 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); 23026 pDevice->wasapi.pMappedBufferCapture = NULL; 23027 pDevice->wasapi.mappedBufferCaptureCap = 0; 23028 pDevice->wasapi.mappedBufferCaptureLen = 0; 23029 } 23030 23031 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); 23032 } 23033 23034 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 23035 /* 23036 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to 23037 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. 23038 */ 23039 if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { 23040 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ 23041 DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; 23042 23043 if (pDevice->playback.shareMode == ma_share_mode_exclusive) { 23044 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); 23045 } 23046 else { 23047 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; 23048 ma_uint32 framesAvailablePlayback; 23049 for (;;) { 23050 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); 23051 if (result != MA_SUCCESS) { 23052 break; 23053 } 23054 23055 if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { 23056 break; 23057 } 23058 23059 /* 23060 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames 23061 has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. 23062 */ 23063 if (framesAvailablePlayback == prevFramesAvaialablePlayback) { 23064 break; 23065 } 23066 prevFramesAvaialablePlayback = framesAvailablePlayback; 23067 23068 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); 23069 ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ 23070 } 23071 } 23072 } 23073 23074 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); 23075 if (FAILED(hr)) { 23076 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); 23077 return ma_result_from_HRESULT(hr); 23078 } 23079 23080 /* The audio client needs to be reset otherwise restarting will fail. */ 23081 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); 23082 if (FAILED(hr)) { 23083 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); 23084 return ma_result_from_HRESULT(hr); 23085 } 23086 23087 if (pDevice->wasapi.pMappedBufferPlayback != NULL) { 23088 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); 23089 pDevice->wasapi.pMappedBufferPlayback = NULL; 23090 pDevice->wasapi.mappedBufferPlaybackCap = 0; 23091 pDevice->wasapi.mappedBufferPlaybackLen = 0; 23092 } 23093 23094 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); 23095 } 23096 23097 return MA_SUCCESS; 23098 } 23099 23100 static ma_result ma_device_stop__wasapi(ma_device* pDevice) 23101 { 23102 ma_result result; 23103 23104 MA_ASSERT(pDevice != NULL); 23105 23106 /* Wait for any rerouting to finish before attempting to stop the device. */ 23107 ma_mutex_lock(&pDevice->wasapi.rerouteLock); 23108 { 23109 result = ma_device_stop__wasapi_nolock(pDevice); 23110 } 23111 ma_mutex_unlock(&pDevice->wasapi.rerouteLock); 23112 23113 return result; 23114 } 23115 23116 23117 #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 23118 #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 23119 #endif 23120 23121 static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 23122 { 23123 ma_result result = MA_SUCCESS; 23124 ma_uint32 totalFramesProcessed = 0; 23125 23126 /* 23127 When reading, we need to get a buffer and process all of it before releasing it. Because the 23128 frame count (frameCount) can be different to the size of the buffer, we'll need to cache the 23129 pointer to the buffer. 23130 */ 23131 23132 /* Keep running until we've processed the requested number of frames. */ 23133 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { 23134 ma_uint32 framesRemaining = frameCount - totalFramesProcessed; 23135 23136 /* If we have a mapped data buffer, consume that first. */ 23137 if (pDevice->wasapi.pMappedBufferCapture != NULL) { 23138 /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ 23139 ma_uint32 framesToProcessNow = framesRemaining; 23140 if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { 23141 framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; 23142 } 23143 23144 /* Now just copy the data over to the output buffer. */ 23145 ma_copy_pcm_frames( 23146 ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), 23147 ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), 23148 framesToProcessNow, 23149 pDevice->capture.internalFormat, pDevice->capture.internalChannels 23150 ); 23151 23152 totalFramesProcessed += framesToProcessNow; 23153 pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; 23154 23155 /* If the data buffer has been fully consumed we need to release it. */ 23156 if (pDevice->wasapi.mappedBufferCaptureLen == 0) { 23157 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); 23158 pDevice->wasapi.pMappedBufferCapture = NULL; 23159 pDevice->wasapi.mappedBufferCaptureCap = 0; 23160 } 23161 } else { 23162 /* We don't have any cached data pointer, so grab another one. */ 23163 HRESULT hr; 23164 DWORD flags = 0; 23165 23166 /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ 23167 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); 23168 if (hr == S_OK) { 23169 /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ 23170 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; 23171 23172 /* 23173 There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every 23174 call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially 23175 work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution 23176 would be to figure out why the flag is always getting reported. 23177 */ 23178 #if defined(MA_DEBUG_OUTPUT) 23179 { 23180 if (flags != 0) { 23181 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); 23182 23183 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { 23184 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); 23185 } 23186 } 23187 } 23188 #endif 23189 23190 /* Overrun detection. */ 23191 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { 23192 /* Glitched. Probably due to an overrun. */ 23193 23194 /* 23195 If we got an overrun it probably means we're straddling the end of the buffer. In normal capture 23196 mode this is the fault of the client application because they're responsible for ensuring data is 23197 processed fast enough. In duplex mode, however, the processing of audio is tied to the playback 23198 device, so this can possibly be the result of a timing de-sync. 23199 23200 In capture mode we're not going to do any kind of recovery because the real fix is for the client 23201 application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers 23202 to prevent a never-ending sequence of glitches due to straddling the end of the buffer. 23203 */ 23204 if (pDevice->type == ma_device_type_duplex) { 23205 /* 23206 Experiment: 23207 23208 If we empty out the *entire* buffer we may end up putting ourselves into an underrun position 23209 which isn't really any better than the overrun we're probably in right now. Instead we'll just 23210 empty out about half. 23211 */ 23212 ma_uint32 i; 23213 ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); 23214 ma_uint32 iterationCount = periodCount / 2; 23215 if ((periodCount % 2) > 0) { 23216 iterationCount += 1; 23217 } 23218 23219 for (i = 0; i < iterationCount; i += 1) { 23220 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); 23221 if (FAILED(hr)) { 23222 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); 23223 break; 23224 } 23225 23226 flags = 0; 23227 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); 23228 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { 23229 /* 23230 The buffer has been completely emptied or an error occurred. In this case we'll need 23231 to reset the state of the mapped buffer which will trigger the next iteration to get 23232 a fresh buffer from WASAPI. 23233 */ 23234 pDevice->wasapi.pMappedBufferCapture = NULL; 23235 pDevice->wasapi.mappedBufferCaptureCap = 0; 23236 pDevice->wasapi.mappedBufferCaptureLen = 0; 23237 23238 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { 23239 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { 23240 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); 23241 } else { 23242 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); 23243 } 23244 } 23245 23246 if (FAILED(hr)) { 23247 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); 23248 } 23249 23250 break; 23251 } 23252 } 23253 23254 /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ 23255 if (pDevice->wasapi.pMappedBufferCapture != NULL) { 23256 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; 23257 } 23258 } 23259 } 23260 23261 continue; 23262 } else { 23263 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { 23264 /* 23265 No data is available. We need to wait for more. There's two situations to consider 23266 here. The first is normal capture mode. If this times out it probably means the 23267 microphone isn't delivering data for whatever reason. In this case we'll just 23268 abort the read and return whatever we were able to get. The other situations is 23269 loopback mode, in which case a timeout probably just means the nothing is playing 23270 through the speakers. 23271 */ 23272 23273 /* Experiment: Use a shorter timeout for loopback mode. */ 23274 DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; 23275 if (pDevice->type == ma_device_type_loopback) { 23276 timeoutInMilliseconds = 10; 23277 } 23278 23279 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { 23280 if (pDevice->type == ma_device_type_loopback) { 23281 continue; /* Keep waiting in loopback mode. */ 23282 } else { 23283 result = MA_ERROR; 23284 break; /* Wait failed. */ 23285 } 23286 } 23287 23288 /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ 23289 } else { 23290 /* An error occurred and we need to abort. */ 23291 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); 23292 result = ma_result_from_HRESULT(hr); 23293 break; 23294 } 23295 } 23296 } 23297 } 23298 23299 /* 23300 If we were unable to process the entire requested frame count, but we still have a mapped buffer, 23301 there's a good chance either an error occurred or the device was stopped mid-read. In this case 23302 we'll need to make sure the buffer is released. 23303 */ 23304 if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { 23305 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); 23306 pDevice->wasapi.pMappedBufferCapture = NULL; 23307 pDevice->wasapi.mappedBufferCaptureCap = 0; 23308 pDevice->wasapi.mappedBufferCaptureLen = 0; 23309 } 23310 23311 if (pFramesRead != NULL) { 23312 *pFramesRead = totalFramesProcessed; 23313 } 23314 23315 return result; 23316 } 23317 23318 static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 23319 { 23320 ma_result result = MA_SUCCESS; 23321 ma_uint32 totalFramesProcessed = 0; 23322 23323 /* Keep writing to the device until it's stopped or we've consumed all of our input. */ 23324 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { 23325 ma_uint32 framesRemaining = frameCount - totalFramesProcessed; 23326 23327 /* 23328 We're going to do this in a similar way to capture. We'll first check if the cached data pointer 23329 is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with 23330 a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE 23331 it means we need to wait for some data to become available. 23332 */ 23333 if (pDevice->wasapi.pMappedBufferPlayback != NULL) { 23334 /* We still have some space available in the mapped data buffer. Write to it. */ 23335 ma_uint32 framesToProcessNow = framesRemaining; 23336 if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { 23337 framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); 23338 } 23339 23340 /* Now just copy the data over to the output buffer. */ 23341 ma_copy_pcm_frames( 23342 ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), 23343 ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), 23344 framesToProcessNow, 23345 pDevice->playback.internalFormat, pDevice->playback.internalChannels 23346 ); 23347 23348 totalFramesProcessed += framesToProcessNow; 23349 pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; 23350 23351 /* If the data buffer has been fully consumed we need to release it. */ 23352 if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { 23353 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); 23354 pDevice->wasapi.pMappedBufferPlayback = NULL; 23355 pDevice->wasapi.mappedBufferPlaybackCap = 0; 23356 pDevice->wasapi.mappedBufferPlaybackLen = 0; 23357 23358 /* 23359 In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never 23360 seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine 23361 whether or not we need to wait for more data. 23362 */ 23363 if (pDevice->playback.shareMode == ma_share_mode_exclusive) { 23364 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { 23365 result = MA_ERROR; 23366 break; /* Wait failed. Probably timed out. */ 23367 } 23368 } 23369 } 23370 } else { 23371 /* We don't have a mapped data buffer so we'll need to get one. */ 23372 HRESULT hr; 23373 ma_uint32 bufferSizeInFrames; 23374 23375 /* Special rules for exclusive mode. */ 23376 if (pDevice->playback.shareMode == ma_share_mode_exclusive) { 23377 bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; 23378 } else { 23379 bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; 23380 } 23381 23382 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); 23383 if (hr == S_OK) { 23384 /* We have data available. */ 23385 pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; 23386 pDevice->wasapi.mappedBufferPlaybackLen = 0; 23387 } else { 23388 if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { 23389 /* Not enough data available. We need to wait for more. */ 23390 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { 23391 result = MA_ERROR; 23392 break; /* Wait failed. Probably timed out. */ 23393 } 23394 } else { 23395 /* Some error occurred. We'll need to abort. */ 23396 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); 23397 result = ma_result_from_HRESULT(hr); 23398 break; 23399 } 23400 } 23401 } 23402 } 23403 23404 if (pFramesWritten != NULL) { 23405 *pFramesWritten = totalFramesProcessed; 23406 } 23407 23408 return result; 23409 } 23410 23411 static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) 23412 { 23413 MA_ASSERT(pDevice != NULL); 23414 23415 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 23416 SetEvent((HANDLE)pDevice->wasapi.hEventCapture); 23417 } 23418 23419 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 23420 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); 23421 } 23422 23423 return MA_SUCCESS; 23424 } 23425 23426 23427 static ma_result ma_context_uninit__wasapi(ma_context* pContext) 23428 { 23429 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); 23430 23431 MA_ASSERT(pContext != NULL); 23432 MA_ASSERT(pContext->backend == ma_backend_wasapi); 23433 23434 ma_context_post_command__wasapi(pContext, &cmd); 23435 ma_thread_wait(&pContext->wasapi.commandThread); 23436 23437 if (pContext->wasapi.hAvrt) { 23438 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); 23439 pContext->wasapi.hAvrt = NULL; 23440 } 23441 23442 #if defined(MA_WIN32_UWP) 23443 { 23444 if (pContext->wasapi.hMMDevapi) { 23445 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); 23446 pContext->wasapi.hMMDevapi = NULL; 23447 } 23448 } 23449 #endif 23450 23451 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ 23452 ma_semaphore_uninit(&pContext->wasapi.commandSem); 23453 ma_mutex_uninit(&pContext->wasapi.commandLock); 23454 23455 return MA_SUCCESS; 23456 } 23457 23458 static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 23459 { 23460 ma_result result = MA_SUCCESS; 23461 23462 MA_ASSERT(pContext != NULL); 23463 23464 (void)pConfig; 23465 23466 #ifdef MA_WIN32_DESKTOP 23467 /* 23468 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven 23469 exclusive mode does not work until SP1. 23470 23471 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. 23472 */ 23473 { 23474 ma_OSVERSIONINFOEXW osvi; 23475 ma_handle kernel32DLL; 23476 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; 23477 ma_PFNVerSetConditionMask _VerSetConditionMask; 23478 23479 kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); 23480 if (kernel32DLL == NULL) { 23481 return MA_NO_BACKEND; 23482 } 23483 23484 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); 23485 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); 23486 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { 23487 ma_dlclose(ma_context_get_log(pContext), kernel32DLL); 23488 return MA_NO_BACKEND; 23489 } 23490 23491 MA_ZERO_OBJECT(&osvi); 23492 osvi.dwOSVersionInfoSize = sizeof(osvi); 23493 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); 23494 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); 23495 osvi.wServicePackMajor = 1; 23496 if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { 23497 result = MA_SUCCESS; 23498 } else { 23499 result = MA_NO_BACKEND; 23500 } 23501 23502 ma_dlclose(ma_context_get_log(pContext), kernel32DLL); 23503 } 23504 #endif 23505 23506 if (result != MA_SUCCESS) { 23507 return result; 23508 } 23509 23510 MA_ZERO_OBJECT(&pContext->wasapi); 23511 23512 23513 #if defined(MA_WIN32_UWP) 23514 { 23515 /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ 23516 pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); 23517 if (pContext->wasapi.hMMDevapi) { 23518 pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); 23519 if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { 23520 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); 23521 return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ 23522 } 23523 } else { 23524 return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ 23525 } 23526 } 23527 #endif 23528 23529 /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ 23530 pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); 23531 if (pContext->wasapi.hAvrt) { 23532 pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); 23533 pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); 23534 23535 /* If either function could not be found, disable use of avrt entirely. */ 23536 if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { 23537 pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; 23538 pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; 23539 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); 23540 pContext->wasapi.hAvrt = NULL; 23541 } 23542 } 23543 23544 23545 /* 23546 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread 23547 than the one that retrieved it with GetService(). This can result in a deadlock in two 23548 situations: 23549 23550 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and 23551 2) When uninitializing and reinitializing the internal IAudioClient object in response to 23552 automatic stream routing. 23553 23554 We could define ma_device_uninit() such that it must be called on the same thread as 23555 ma_device_init(). We could also just not release the IAudioClient when performing automatic 23556 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so 23557 we're going to have to work around this with a worker thread. This is not ideal, but I can't 23558 think of a better way to do this. 23559 23560 More information about this can be found here: 23561 23562 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient 23563 23564 Note this section: 23565 23566 When releasing an IAudioRenderClient interface instance, the client must call the interface's 23567 Release method from the same thread as the call to IAudioClient::GetService that created the 23568 object. 23569 */ 23570 { 23571 result = ma_mutex_init(&pContext->wasapi.commandLock); 23572 if (result != MA_SUCCESS) { 23573 return result; 23574 } 23575 23576 result = ma_semaphore_init(0, &pContext->wasapi.commandSem); 23577 if (result != MA_SUCCESS) { 23578 ma_mutex_uninit(&pContext->wasapi.commandLock); 23579 return result; 23580 } 23581 23582 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); 23583 if (result != MA_SUCCESS) { 23584 ma_semaphore_uninit(&pContext->wasapi.commandSem); 23585 ma_mutex_uninit(&pContext->wasapi.commandLock); 23586 return result; 23587 } 23588 } 23589 23590 23591 pCallbacks->onContextInit = ma_context_init__wasapi; 23592 pCallbacks->onContextUninit = ma_context_uninit__wasapi; 23593 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; 23594 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; 23595 pCallbacks->onDeviceInit = ma_device_init__wasapi; 23596 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; 23597 pCallbacks->onDeviceStart = ma_device_start__wasapi; 23598 pCallbacks->onDeviceStop = ma_device_stop__wasapi; 23599 pCallbacks->onDeviceRead = ma_device_read__wasapi; 23600 pCallbacks->onDeviceWrite = ma_device_write__wasapi; 23601 pCallbacks->onDeviceDataLoop = NULL; 23602 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; 23603 23604 return MA_SUCCESS; 23605 } 23606 #endif 23607 23608 /****************************************************************************** 23609 23610 DirectSound Backend 23611 23612 ******************************************************************************/ 23613 #ifdef MA_HAS_DSOUND 23614 /*#include <dsound.h>*/ 23615 23616 /*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ 23617 23618 /* miniaudio only uses priority or exclusive modes. */ 23619 #define MA_DSSCL_NORMAL 1 23620 #define MA_DSSCL_PRIORITY 2 23621 #define MA_DSSCL_EXCLUSIVE 3 23622 #define MA_DSSCL_WRITEPRIMARY 4 23623 23624 #define MA_DSCAPS_PRIMARYMONO 0x00000001 23625 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002 23626 #define MA_DSCAPS_PRIMARY8BIT 0x00000004 23627 #define MA_DSCAPS_PRIMARY16BIT 0x00000008 23628 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010 23629 #define MA_DSCAPS_EMULDRIVER 0x00000020 23630 #define MA_DSCAPS_CERTIFIED 0x00000040 23631 #define MA_DSCAPS_SECONDARYMONO 0x00000100 23632 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200 23633 #define MA_DSCAPS_SECONDARY8BIT 0x00000400 23634 #define MA_DSCAPS_SECONDARY16BIT 0x00000800 23635 23636 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 23637 #define MA_DSBCAPS_STATIC 0x00000002 23638 #define MA_DSBCAPS_LOCHARDWARE 0x00000004 23639 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008 23640 #define MA_DSBCAPS_CTRL3D 0x00000010 23641 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 23642 #define MA_DSBCAPS_CTRLPAN 0x00000040 23643 #define MA_DSBCAPS_CTRLVOLUME 0x00000080 23644 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 23645 #define MA_DSBCAPS_CTRLFX 0x00000200 23646 #define MA_DSBCAPS_STICKYFOCUS 0x00004000 23647 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000 23648 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 23649 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 23650 #define MA_DSBCAPS_LOCDEFER 0x00040000 23651 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 23652 23653 #define MA_DSBPLAY_LOOPING 0x00000001 23654 #define MA_DSBPLAY_LOCHARDWARE 0x00000002 23655 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004 23656 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 23657 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 23658 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 23659 23660 #define MA_DSCBSTART_LOOPING 0x00000001 23661 23662 typedef struct 23663 { 23664 DWORD dwSize; 23665 DWORD dwFlags; 23666 DWORD dwBufferBytes; 23667 DWORD dwReserved; 23668 MA_WAVEFORMATEX* lpwfxFormat; 23669 GUID guid3DAlgorithm; 23670 } MA_DSBUFFERDESC; 23671 23672 typedef struct 23673 { 23674 DWORD dwSize; 23675 DWORD dwFlags; 23676 DWORD dwBufferBytes; 23677 DWORD dwReserved; 23678 MA_WAVEFORMATEX* lpwfxFormat; 23679 DWORD dwFXCount; 23680 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ 23681 } MA_DSCBUFFERDESC; 23682 23683 typedef struct 23684 { 23685 DWORD dwSize; 23686 DWORD dwFlags; 23687 DWORD dwMinSecondarySampleRate; 23688 DWORD dwMaxSecondarySampleRate; 23689 DWORD dwPrimaryBuffers; 23690 DWORD dwMaxHwMixingAllBuffers; 23691 DWORD dwMaxHwMixingStaticBuffers; 23692 DWORD dwMaxHwMixingStreamingBuffers; 23693 DWORD dwFreeHwMixingAllBuffers; 23694 DWORD dwFreeHwMixingStaticBuffers; 23695 DWORD dwFreeHwMixingStreamingBuffers; 23696 DWORD dwMaxHw3DAllBuffers; 23697 DWORD dwMaxHw3DStaticBuffers; 23698 DWORD dwMaxHw3DStreamingBuffers; 23699 DWORD dwFreeHw3DAllBuffers; 23700 DWORD dwFreeHw3DStaticBuffers; 23701 DWORD dwFreeHw3DStreamingBuffers; 23702 DWORD dwTotalHwMemBytes; 23703 DWORD dwFreeHwMemBytes; 23704 DWORD dwMaxContigFreeHwMemBytes; 23705 DWORD dwUnlockTransferRateHwBuffers; 23706 DWORD dwPlayCpuOverheadSwBuffers; 23707 DWORD dwReserved1; 23708 DWORD dwReserved2; 23709 } MA_DSCAPS; 23710 23711 typedef struct 23712 { 23713 DWORD dwSize; 23714 DWORD dwFlags; 23715 DWORD dwBufferBytes; 23716 DWORD dwUnlockTransferRate; 23717 DWORD dwPlayCpuOverhead; 23718 } MA_DSBCAPS; 23719 23720 typedef struct 23721 { 23722 DWORD dwSize; 23723 DWORD dwFlags; 23724 DWORD dwFormats; 23725 DWORD dwChannels; 23726 } MA_DSCCAPS; 23727 23728 typedef struct 23729 { 23730 DWORD dwSize; 23731 DWORD dwFlags; 23732 DWORD dwBufferBytes; 23733 DWORD dwReserved; 23734 } MA_DSCBCAPS; 23735 23736 typedef struct 23737 { 23738 DWORD dwOffset; 23739 HANDLE hEventNotify; 23740 } MA_DSBPOSITIONNOTIFY; 23741 23742 typedef struct ma_IDirectSound ma_IDirectSound; 23743 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; 23744 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; 23745 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; 23746 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; 23747 23748 23749 /* 23750 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of 23751 like how C++ works internally), and then you have a structure with a single member, which is a 23752 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need 23753 to be in a specific order, and parent classes need to have their methods declared first. 23754 */ 23755 23756 /* IDirectSound */ 23757 typedef struct 23758 { 23759 /* IUnknown */ 23760 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); 23761 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); 23762 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); 23763 23764 /* IDirectSound */ 23765 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); 23766 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); 23767 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); 23768 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); 23769 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); 23770 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); 23771 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); 23772 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); 23773 } ma_IDirectSoundVtbl; 23774 struct ma_IDirectSound 23775 { 23776 ma_IDirectSoundVtbl* lpVtbl; 23777 }; 23778 static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 23779 static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } 23780 static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } 23781 static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } 23782 static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } 23783 static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } 23784 static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } 23785 static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } 23786 static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } 23787 static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } 23788 static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } 23789 23790 23791 /* IDirectSoundBuffer */ 23792 typedef struct 23793 { 23794 /* IUnknown */ 23795 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); 23796 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); 23797 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); 23798 23799 /* IDirectSoundBuffer */ 23800 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); 23801 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); 23802 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); 23803 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); 23804 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); 23805 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); 23806 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); 23807 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); 23808 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); 23809 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); 23810 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); 23811 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); 23812 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); 23813 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); 23814 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); 23815 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); 23816 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); 23817 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); 23818 } ma_IDirectSoundBufferVtbl; 23819 struct ma_IDirectSoundBuffer 23820 { 23821 ma_IDirectSoundBufferVtbl* lpVtbl; 23822 }; 23823 static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 23824 static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } 23825 static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } 23826 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } 23827 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } 23828 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } 23829 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } 23830 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } 23831 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } 23832 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } 23833 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } 23834 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } 23835 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } 23836 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } 23837 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } 23838 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } 23839 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } 23840 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } 23841 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } 23842 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } 23843 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } 23844 23845 23846 /* IDirectSoundCapture */ 23847 typedef struct 23848 { 23849 /* IUnknown */ 23850 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); 23851 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); 23852 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); 23853 23854 /* IDirectSoundCapture */ 23855 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); 23856 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); 23857 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); 23858 } ma_IDirectSoundCaptureVtbl; 23859 struct ma_IDirectSoundCapture 23860 { 23861 ma_IDirectSoundCaptureVtbl* lpVtbl; 23862 }; 23863 static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 23864 static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } 23865 static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } 23866 static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } 23867 static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } 23868 static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } 23869 23870 23871 /* IDirectSoundCaptureBuffer */ 23872 typedef struct 23873 { 23874 /* IUnknown */ 23875 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); 23876 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); 23877 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); 23878 23879 /* IDirectSoundCaptureBuffer */ 23880 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); 23881 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); 23882 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); 23883 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); 23884 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); 23885 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); 23886 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); 23887 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); 23888 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); 23889 } ma_IDirectSoundCaptureBufferVtbl; 23890 struct ma_IDirectSoundCaptureBuffer 23891 { 23892 ma_IDirectSoundCaptureBufferVtbl* lpVtbl; 23893 }; 23894 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 23895 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } 23896 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } 23897 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } 23898 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } 23899 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } 23900 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } 23901 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } 23902 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } 23903 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } 23904 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } 23905 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } 23906 23907 23908 /* IDirectSoundNotify */ 23909 typedef struct 23910 { 23911 /* IUnknown */ 23912 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); 23913 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); 23914 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); 23915 23916 /* IDirectSoundNotify */ 23917 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); 23918 } ma_IDirectSoundNotifyVtbl; 23919 struct ma_IDirectSoundNotify 23920 { 23921 ma_IDirectSoundNotifyVtbl* lpVtbl; 23922 }; 23923 static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } 23924 static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } 23925 static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } 23926 static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } 23927 23928 23929 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); 23930 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); 23931 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); 23932 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); 23933 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); 23934 23935 static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) 23936 { 23937 /* Normalize the range in case we were given something stupid. */ 23938 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { 23939 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; 23940 } 23941 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { 23942 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; 23943 } 23944 if (sampleRateMin > sampleRateMax) { 23945 sampleRateMin = sampleRateMax; 23946 } 23947 23948 if (sampleRateMin == sampleRateMax) { 23949 return sampleRateMax; 23950 } else { 23951 size_t iStandardRate; 23952 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { 23953 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; 23954 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { 23955 return standardRate; 23956 } 23957 } 23958 } 23959 23960 /* Should never get here. */ 23961 MA_ASSERT(MA_FALSE); 23962 return 0; 23963 } 23964 23965 /* 23966 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, 23967 the channel count and channel map will be left unmodified. 23968 */ 23969 static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) 23970 { 23971 WORD channels; 23972 DWORD channelMap; 23973 23974 channels = 0; 23975 if (pChannelsOut != NULL) { 23976 channels = *pChannelsOut; 23977 } 23978 23979 channelMap = 0; 23980 if (pChannelMapOut != NULL) { 23981 channelMap = *pChannelMapOut; 23982 } 23983 23984 /* 23985 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper 23986 16 bits is for the geometry. 23987 */ 23988 switch ((BYTE)(speakerConfig)) { 23989 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; 23990 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; 23991 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; 23992 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; 23993 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; 23994 case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; 23995 case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; 23996 case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; 23997 case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; 23998 default: break; 23999 } 24000 24001 if (pChannelsOut != NULL) { 24002 *pChannelsOut = channels; 24003 } 24004 24005 if (pChannelMapOut != NULL) { 24006 *pChannelMapOut = channelMap; 24007 } 24008 } 24009 24010 24011 static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) 24012 { 24013 ma_IDirectSound* pDirectSound; 24014 HWND hWnd; 24015 HRESULT hr; 24016 24017 MA_ASSERT(pContext != NULL); 24018 MA_ASSERT(ppDirectSound != NULL); 24019 24020 *ppDirectSound = NULL; 24021 pDirectSound = NULL; 24022 24023 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { 24024 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); 24025 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 24026 } 24027 24028 /* The cooperative level must be set before doing anything else. */ 24029 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); 24030 if (hWnd == 0) { 24031 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); 24032 } 24033 24034 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); 24035 if (FAILED(hr)) { 24036 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); 24037 return ma_result_from_HRESULT(hr); 24038 } 24039 24040 *ppDirectSound = pDirectSound; 24041 return MA_SUCCESS; 24042 } 24043 24044 static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) 24045 { 24046 ma_IDirectSoundCapture* pDirectSoundCapture; 24047 HRESULT hr; 24048 24049 MA_ASSERT(pContext != NULL); 24050 MA_ASSERT(ppDirectSoundCapture != NULL); 24051 24052 /* DirectSound does not support exclusive mode for capture. */ 24053 if (shareMode == ma_share_mode_exclusive) { 24054 return MA_SHARE_MODE_NOT_SUPPORTED; 24055 } 24056 24057 *ppDirectSoundCapture = NULL; 24058 pDirectSoundCapture = NULL; 24059 24060 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); 24061 if (FAILED(hr)) { 24062 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); 24063 return ma_result_from_HRESULT(hr); 24064 } 24065 24066 *ppDirectSoundCapture = pDirectSoundCapture; 24067 return MA_SUCCESS; 24068 } 24069 24070 static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) 24071 { 24072 HRESULT hr; 24073 MA_DSCCAPS caps; 24074 WORD bitsPerSample; 24075 DWORD sampleRate; 24076 24077 MA_ASSERT(pContext != NULL); 24078 MA_ASSERT(pDirectSoundCapture != NULL); 24079 24080 if (pChannels) { 24081 *pChannels = 0; 24082 } 24083 if (pBitsPerSample) { 24084 *pBitsPerSample = 0; 24085 } 24086 if (pSampleRate) { 24087 *pSampleRate = 0; 24088 } 24089 24090 MA_ZERO_OBJECT(&caps); 24091 caps.dwSize = sizeof(caps); 24092 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); 24093 if (FAILED(hr)) { 24094 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); 24095 return ma_result_from_HRESULT(hr); 24096 } 24097 24098 if (pChannels) { 24099 *pChannels = (WORD)caps.dwChannels; 24100 } 24101 24102 /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ 24103 bitsPerSample = 16; 24104 sampleRate = 48000; 24105 24106 if (caps.dwChannels == 1) { 24107 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { 24108 sampleRate = 48000; 24109 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { 24110 sampleRate = 44100; 24111 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { 24112 sampleRate = 22050; 24113 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { 24114 sampleRate = 11025; 24115 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { 24116 sampleRate = 96000; 24117 } else { 24118 bitsPerSample = 8; 24119 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { 24120 sampleRate = 48000; 24121 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { 24122 sampleRate = 44100; 24123 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { 24124 sampleRate = 22050; 24125 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { 24126 sampleRate = 11025; 24127 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { 24128 sampleRate = 96000; 24129 } else { 24130 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ 24131 } 24132 } 24133 } else if (caps.dwChannels == 2) { 24134 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { 24135 sampleRate = 48000; 24136 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { 24137 sampleRate = 44100; 24138 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { 24139 sampleRate = 22050; 24140 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { 24141 sampleRate = 11025; 24142 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { 24143 sampleRate = 96000; 24144 } else { 24145 bitsPerSample = 8; 24146 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { 24147 sampleRate = 48000; 24148 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { 24149 sampleRate = 44100; 24150 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { 24151 sampleRate = 22050; 24152 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { 24153 sampleRate = 11025; 24154 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { 24155 sampleRate = 96000; 24156 } else { 24157 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ 24158 } 24159 } 24160 } 24161 24162 if (pBitsPerSample) { 24163 *pBitsPerSample = bitsPerSample; 24164 } 24165 if (pSampleRate) { 24166 *pSampleRate = sampleRate; 24167 } 24168 24169 return MA_SUCCESS; 24170 } 24171 24172 24173 typedef struct 24174 { 24175 ma_context* pContext; 24176 ma_device_type deviceType; 24177 ma_enum_devices_callback_proc callback; 24178 void* pUserData; 24179 ma_bool32 terminated; 24180 } ma_context_enumerate_devices_callback_data__dsound; 24181 24182 static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) 24183 { 24184 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; 24185 ma_device_info deviceInfo; 24186 24187 (void)lpcstrModule; 24188 24189 MA_ZERO_OBJECT(&deviceInfo); 24190 24191 /* ID. */ 24192 if (lpGuid != NULL) { 24193 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); 24194 } else { 24195 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); 24196 deviceInfo.isDefault = MA_TRUE; 24197 } 24198 24199 /* Name / Description */ 24200 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); 24201 24202 24203 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ 24204 MA_ASSERT(pData != NULL); 24205 pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); 24206 if (pData->terminated) { 24207 return FALSE; /* Stop enumeration. */ 24208 } else { 24209 return TRUE; /* Continue enumeration. */ 24210 } 24211 } 24212 24213 static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 24214 { 24215 ma_context_enumerate_devices_callback_data__dsound data; 24216 24217 MA_ASSERT(pContext != NULL); 24218 MA_ASSERT(callback != NULL); 24219 24220 data.pContext = pContext; 24221 data.callback = callback; 24222 data.pUserData = pUserData; 24223 data.terminated = MA_FALSE; 24224 24225 /* Playback. */ 24226 if (!data.terminated) { 24227 data.deviceType = ma_device_type_playback; 24228 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); 24229 } 24230 24231 /* Capture. */ 24232 if (!data.terminated) { 24233 data.deviceType = ma_device_type_capture; 24234 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); 24235 } 24236 24237 return MA_SUCCESS; 24238 } 24239 24240 24241 typedef struct 24242 { 24243 const ma_device_id* pDeviceID; 24244 ma_device_info* pDeviceInfo; 24245 ma_bool32 found; 24246 } ma_context_get_device_info_callback_data__dsound; 24247 24248 static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) 24249 { 24250 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; 24251 MA_ASSERT(pData != NULL); 24252 24253 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { 24254 /* Default device. */ 24255 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); 24256 pData->pDeviceInfo->isDefault = MA_TRUE; 24257 pData->found = MA_TRUE; 24258 return FALSE; /* Stop enumeration. */ 24259 } else { 24260 /* Not the default device. */ 24261 if (lpGuid != NULL && pData->pDeviceID != NULL) { 24262 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { 24263 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); 24264 pData->found = MA_TRUE; 24265 return FALSE; /* Stop enumeration. */ 24266 } 24267 } 24268 } 24269 24270 (void)lpcstrModule; 24271 return TRUE; 24272 } 24273 24274 static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 24275 { 24276 ma_result result; 24277 HRESULT hr; 24278 24279 if (pDeviceID != NULL) { 24280 ma_context_get_device_info_callback_data__dsound data; 24281 24282 /* ID. */ 24283 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); 24284 24285 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ 24286 data.pDeviceID = pDeviceID; 24287 data.pDeviceInfo = pDeviceInfo; 24288 data.found = MA_FALSE; 24289 if (deviceType == ma_device_type_playback) { 24290 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); 24291 } else { 24292 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); 24293 } 24294 24295 if (!data.found) { 24296 return MA_NO_DEVICE; 24297 } 24298 } else { 24299 /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ 24300 24301 /* ID */ 24302 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); 24303 24304 /* Name / Description */ 24305 if (deviceType == ma_device_type_playback) { 24306 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 24307 } else { 24308 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 24309 } 24310 24311 pDeviceInfo->isDefault = MA_TRUE; 24312 } 24313 24314 /* Retrieving detailed information is slightly different depending on the device type. */ 24315 if (deviceType == ma_device_type_playback) { 24316 /* Playback. */ 24317 ma_IDirectSound* pDirectSound; 24318 MA_DSCAPS caps; 24319 WORD channels; 24320 24321 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); 24322 if (result != MA_SUCCESS) { 24323 return result; 24324 } 24325 24326 MA_ZERO_OBJECT(&caps); 24327 caps.dwSize = sizeof(caps); 24328 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); 24329 if (FAILED(hr)) { 24330 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); 24331 return ma_result_from_HRESULT(hr); 24332 } 24333 24334 24335 /* Channels. Only a single channel count is reported for DirectSound. */ 24336 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { 24337 /* It supports at least stereo, but could support more. */ 24338 DWORD speakerConfig; 24339 24340 channels = 2; 24341 24342 /* Look at the speaker configuration to get a better idea on the channel count. */ 24343 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); 24344 if (SUCCEEDED(hr)) { 24345 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); 24346 } 24347 } else { 24348 /* It does not support stereo, which means we are stuck with mono. */ 24349 channels = 1; 24350 } 24351 24352 24353 /* 24354 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel 24355 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio 24356 in order to keep the size of this within reason. 24357 */ 24358 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { 24359 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ 24360 size_t iStandardSampleRate; 24361 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { 24362 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; 24363 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { 24364 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; 24365 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 24366 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; 24367 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; 24368 pDeviceInfo->nativeDataFormatCount += 1; 24369 } 24370 } 24371 } else { 24372 /* Only a single sample rate is supported. */ 24373 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; 24374 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 24375 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; 24376 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; 24377 pDeviceInfo->nativeDataFormatCount += 1; 24378 } 24379 24380 ma_IDirectSound_Release(pDirectSound); 24381 } else { 24382 /* 24383 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture 24384 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just 24385 reporting the best format. 24386 */ 24387 ma_IDirectSoundCapture* pDirectSoundCapture; 24388 WORD channels; 24389 WORD bitsPerSample; 24390 DWORD sampleRate; 24391 24392 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); 24393 if (result != MA_SUCCESS) { 24394 return result; 24395 } 24396 24397 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); 24398 if (result != MA_SUCCESS) { 24399 ma_IDirectSoundCapture_Release(pDirectSoundCapture); 24400 return result; 24401 } 24402 24403 ma_IDirectSoundCapture_Release(pDirectSoundCapture); 24404 24405 /* The format is always an integer format and is based on the bits per sample. */ 24406 if (bitsPerSample == 8) { 24407 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; 24408 } else if (bitsPerSample == 16) { 24409 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; 24410 } else if (bitsPerSample == 24) { 24411 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; 24412 } else if (bitsPerSample == 32) { 24413 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; 24414 } else { 24415 return MA_FORMAT_NOT_SUPPORTED; 24416 } 24417 24418 pDeviceInfo->nativeDataFormats[0].channels = channels; 24419 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; 24420 pDeviceInfo->nativeDataFormats[0].flags = 0; 24421 pDeviceInfo->nativeDataFormatCount = 1; 24422 } 24423 24424 return MA_SUCCESS; 24425 } 24426 24427 24428 24429 static ma_result ma_device_uninit__dsound(ma_device* pDevice) 24430 { 24431 MA_ASSERT(pDevice != NULL); 24432 24433 if (pDevice->dsound.pCaptureBuffer != NULL) { 24434 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); 24435 } 24436 if (pDevice->dsound.pCapture != NULL) { 24437 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); 24438 } 24439 24440 if (pDevice->dsound.pPlaybackBuffer != NULL) { 24441 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); 24442 } 24443 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { 24444 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); 24445 } 24446 if (pDevice->dsound.pPlayback != NULL) { 24447 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); 24448 } 24449 24450 return MA_SUCCESS; 24451 } 24452 24453 static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) 24454 { 24455 GUID subformat; 24456 24457 if (format == ma_format_unknown) { 24458 format = MA_DEFAULT_FORMAT; 24459 } 24460 24461 if (channels == 0) { 24462 channels = MA_DEFAULT_CHANNELS; 24463 } 24464 24465 if (sampleRate == 0) { 24466 sampleRate = MA_DEFAULT_SAMPLE_RATE; 24467 } 24468 24469 switch (format) 24470 { 24471 case ma_format_u8: 24472 case ma_format_s16: 24473 case ma_format_s24: 24474 /*case ma_format_s24_32:*/ 24475 case ma_format_s32: 24476 { 24477 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; 24478 } break; 24479 24480 case ma_format_f32: 24481 { 24482 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; 24483 } break; 24484 24485 default: 24486 return MA_FORMAT_NOT_SUPPORTED; 24487 } 24488 24489 MA_ZERO_OBJECT(pWF); 24490 pWF->cbSize = sizeof(*pWF); 24491 pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; 24492 pWF->nChannels = (WORD)channels; 24493 pWF->nSamplesPerSec = (DWORD)sampleRate; 24494 pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); 24495 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); 24496 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; 24497 pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; 24498 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); 24499 pWF->SubFormat = subformat; 24500 24501 return MA_SUCCESS; 24502 } 24503 24504 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) 24505 { 24506 /* 24507 DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for 24508 reliable glitch-free processing so going to use 30ms instead. 24509 */ 24510 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); 24511 ma_uint32 periodSizeInFrames; 24512 24513 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); 24514 if (periodSizeInFrames < minPeriodSizeInFrames) { 24515 periodSizeInFrames = minPeriodSizeInFrames; 24516 } 24517 24518 return periodSizeInFrames; 24519 } 24520 24521 static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 24522 { 24523 ma_result result; 24524 HRESULT hr; 24525 24526 MA_ASSERT(pDevice != NULL); 24527 24528 MA_ZERO_OBJECT(&pDevice->dsound); 24529 24530 if (pConfig->deviceType == ma_device_type_loopback) { 24531 return MA_DEVICE_TYPE_NOT_SUPPORTED; 24532 } 24533 24534 /* 24535 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize 24536 the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using 24537 full-duplex mode. 24538 */ 24539 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 24540 MA_WAVEFORMATEXTENSIBLE wf; 24541 MA_DSCBUFFERDESC descDS; 24542 ma_uint32 periodSizeInFrames; 24543 ma_uint32 periodCount; 24544 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ 24545 MA_WAVEFORMATEXTENSIBLE* pActualFormat; 24546 24547 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); 24548 if (result != MA_SUCCESS) { 24549 return result; 24550 } 24551 24552 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); 24553 if (result != MA_SUCCESS) { 24554 ma_device_uninit__dsound(pDevice); 24555 return result; 24556 } 24557 24558 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); 24559 if (result != MA_SUCCESS) { 24560 ma_device_uninit__dsound(pDevice); 24561 return result; 24562 } 24563 24564 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); 24565 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; 24566 wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; 24567 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; 24568 24569 /* The size of the buffer must be a clean multiple of the period count. */ 24570 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); 24571 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; 24572 24573 MA_ZERO_OBJECT(&descDS); 24574 descDS.dwSize = sizeof(descDS); 24575 descDS.dwFlags = 0; 24576 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; 24577 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; 24578 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); 24579 if (FAILED(hr)) { 24580 ma_device_uninit__dsound(pDevice); 24581 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); 24582 return ma_result_from_HRESULT(hr); 24583 } 24584 24585 /* Get the _actual_ properties of the buffer. */ 24586 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; 24587 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); 24588 if (FAILED(hr)) { 24589 ma_device_uninit__dsound(pDevice); 24590 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); 24591 return ma_result_from_HRESULT(hr); 24592 } 24593 24594 /* We can now start setting the output data formats. */ 24595 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); 24596 pDescriptorCapture->channels = pActualFormat->nChannels; 24597 pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; 24598 24599 /* Get the native channel map based on the channel mask. */ 24600 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { 24601 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); 24602 } else { 24603 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); 24604 } 24605 24606 /* 24607 After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the 24608 user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. 24609 */ 24610 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { 24611 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; 24612 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); 24613 24614 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); 24615 if (FAILED(hr)) { 24616 ma_device_uninit__dsound(pDevice); 24617 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); 24618 return ma_result_from_HRESULT(hr); 24619 } 24620 } 24621 24622 /* DirectSound should give us a buffer exactly the size we asked for. */ 24623 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; 24624 pDescriptorCapture->periodCount = periodCount; 24625 } 24626 24627 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 24628 MA_WAVEFORMATEXTENSIBLE wf; 24629 MA_DSBUFFERDESC descDSPrimary; 24630 MA_DSCAPS caps; 24631 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ 24632 MA_WAVEFORMATEXTENSIBLE* pActualFormat; 24633 ma_uint32 periodSizeInFrames; 24634 ma_uint32 periodCount; 24635 MA_DSBUFFERDESC descDS; 24636 WORD nativeChannelCount; 24637 DWORD nativeChannelMask = 0; 24638 24639 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); 24640 if (result != MA_SUCCESS) { 24641 return result; 24642 } 24643 24644 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); 24645 if (result != MA_SUCCESS) { 24646 ma_device_uninit__dsound(pDevice); 24647 return result; 24648 } 24649 24650 MA_ZERO_OBJECT(&descDSPrimary); 24651 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); 24652 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; 24653 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); 24654 if (FAILED(hr)) { 24655 ma_device_uninit__dsound(pDevice); 24656 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); 24657 return ma_result_from_HRESULT(hr); 24658 } 24659 24660 24661 /* We may want to make some adjustments to the format if we are using defaults. */ 24662 MA_ZERO_OBJECT(&caps); 24663 caps.dwSize = sizeof(caps); 24664 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); 24665 if (FAILED(hr)) { 24666 ma_device_uninit__dsound(pDevice); 24667 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); 24668 return ma_result_from_HRESULT(hr); 24669 } 24670 24671 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { 24672 DWORD speakerConfig; 24673 24674 /* It supports at least stereo, but could support more. */ 24675 nativeChannelCount = 2; 24676 24677 /* Look at the speaker configuration to get a better idea on the channel count. */ 24678 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { 24679 ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); 24680 } 24681 } else { 24682 /* It does not support stereo, which means we are stuck with mono. */ 24683 nativeChannelCount = 1; 24684 nativeChannelMask = 0x00000001; 24685 } 24686 24687 if (pDescriptorPlayback->channels == 0) { 24688 wf.nChannels = nativeChannelCount; 24689 wf.dwChannelMask = nativeChannelMask; 24690 } 24691 24692 if (pDescriptorPlayback->sampleRate == 0) { 24693 /* We base the sample rate on the values returned by GetCaps(). */ 24694 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { 24695 wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); 24696 } else { 24697 wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; 24698 } 24699 } 24700 24701 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); 24702 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; 24703 24704 /* 24705 From MSDN: 24706 24707 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest 24708 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer 24709 and compare the result with the format that was requested with the SetFormat method. 24710 */ 24711 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); 24712 if (FAILED(hr)) { 24713 /* 24714 If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have 24715 observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a 24716 sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will 24717 use 44100 for the sample rate. 24718 */ 24719 wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ 24720 wf.wFormatTag = WAVE_FORMAT_PCM; 24721 wf.wBitsPerSample = 16; 24722 wf.nChannels = nativeChannelCount; 24723 wf.nSamplesPerSec = 44100; 24724 wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); 24725 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; 24726 24727 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); 24728 if (FAILED(hr)) { 24729 ma_device_uninit__dsound(pDevice); 24730 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); 24731 return ma_result_from_HRESULT(hr); 24732 } 24733 } 24734 24735 /* Get the _actual_ properties of the buffer. */ 24736 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; 24737 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); 24738 if (FAILED(hr)) { 24739 ma_device_uninit__dsound(pDevice); 24740 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); 24741 return ma_result_from_HRESULT(hr); 24742 } 24743 24744 /* We now have enough information to start setting some output properties. */ 24745 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); 24746 pDescriptorPlayback->channels = pActualFormat->nChannels; 24747 pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; 24748 24749 /* Get the internal channel map based on the channel mask. */ 24750 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { 24751 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); 24752 } else { 24753 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); 24754 } 24755 24756 /* The size of the buffer must be a clean multiple of the period count. */ 24757 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); 24758 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; 24759 24760 /* 24761 Meaning of dwFlags (from MSDN): 24762 24763 DSBCAPS_CTRLPOSITIONNOTIFY 24764 The buffer has position notification capability. 24765 24766 DSBCAPS_GLOBALFOCUS 24767 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to 24768 another application, even if the new application uses DirectSound. 24769 24770 DSBCAPS_GETCURRENTPOSITION2 24771 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated 24772 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the 24773 application can get a more accurate play cursor. 24774 */ 24775 MA_ZERO_OBJECT(&descDS); 24776 descDS.dwSize = sizeof(descDS); 24777 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; 24778 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); 24779 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; 24780 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); 24781 if (FAILED(hr)) { 24782 ma_device_uninit__dsound(pDevice); 24783 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); 24784 return ma_result_from_HRESULT(hr); 24785 } 24786 24787 /* DirectSound should give us a buffer exactly the size we asked for. */ 24788 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; 24789 pDescriptorPlayback->periodCount = periodCount; 24790 } 24791 24792 return MA_SUCCESS; 24793 } 24794 24795 24796 static ma_result ma_device_data_loop__dsound(ma_device* pDevice) 24797 { 24798 ma_result result = MA_SUCCESS; 24799 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 24800 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 24801 HRESULT hr; 24802 DWORD lockOffsetInBytesCapture; 24803 DWORD lockSizeInBytesCapture; 24804 DWORD mappedSizeInBytesCapture; 24805 DWORD mappedDeviceFramesProcessedCapture; 24806 void* pMappedDeviceBufferCapture; 24807 DWORD lockOffsetInBytesPlayback; 24808 DWORD lockSizeInBytesPlayback; 24809 DWORD mappedSizeInBytesPlayback; 24810 void* pMappedDeviceBufferPlayback; 24811 DWORD prevReadCursorInBytesCapture = 0; 24812 DWORD prevPlayCursorInBytesPlayback = 0; 24813 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; 24814 DWORD virtualWriteCursorInBytesPlayback = 0; 24815 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; 24816 ma_bool32 isPlaybackDeviceStarted = MA_FALSE; 24817 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ 24818 ma_uint32 waitTimeInMilliseconds = 1; 24819 24820 MA_ASSERT(pDevice != NULL); 24821 24822 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ 24823 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 24824 hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); 24825 if (FAILED(hr)) { 24826 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); 24827 return ma_result_from_HRESULT(hr); 24828 } 24829 } 24830 24831 while (ma_device_get_state(pDevice) == ma_device_state_started) { 24832 switch (pDevice->type) 24833 { 24834 case ma_device_type_duplex: 24835 { 24836 DWORD physicalCaptureCursorInBytes; 24837 DWORD physicalReadCursorInBytes; 24838 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); 24839 if (FAILED(hr)) { 24840 return ma_result_from_HRESULT(hr); 24841 } 24842 24843 /* If nothing is available we just sleep for a bit and return from this iteration. */ 24844 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { 24845 ma_sleep(waitTimeInMilliseconds); 24846 continue; /* Nothing is available in the capture buffer. */ 24847 } 24848 24849 /* 24850 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure 24851 we don't return until every frame has been copied over. 24852 */ 24853 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { 24854 /* The capture position has not looped. This is the simple case. */ 24855 lockOffsetInBytesCapture = prevReadCursorInBytesCapture; 24856 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); 24857 } else { 24858 /* 24859 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, 24860 do it again from the start. 24861 */ 24862 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { 24863 /* Lock up to the end of the buffer. */ 24864 lockOffsetInBytesCapture = prevReadCursorInBytesCapture; 24865 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; 24866 } else { 24867 /* Lock starting from the start of the buffer. */ 24868 lockOffsetInBytesCapture = 0; 24869 lockSizeInBytesCapture = physicalReadCursorInBytes; 24870 } 24871 } 24872 24873 if (lockSizeInBytesCapture == 0) { 24874 ma_sleep(waitTimeInMilliseconds); 24875 continue; /* Nothing is available in the capture buffer. */ 24876 } 24877 24878 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); 24879 if (FAILED(hr)) { 24880 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); 24881 return ma_result_from_HRESULT(hr); 24882 } 24883 24884 24885 /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ 24886 mappedDeviceFramesProcessedCapture = 0; 24887 24888 for (;;) { /* Keep writing to the playback device. */ 24889 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 24890 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 24891 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 24892 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 24893 ma_uint32 outputFramesInClientFormatCount; 24894 ma_uint32 outputFramesInClientFormatConsumed = 0; 24895 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); 24896 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; 24897 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); 24898 24899 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); 24900 if (result != MA_SUCCESS) { 24901 break; 24902 } 24903 24904 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; 24905 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; 24906 24907 ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); 24908 24909 /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ 24910 for (;;) { 24911 ma_uint32 framesWrittenThisIteration; 24912 DWORD physicalPlayCursorInBytes; 24913 DWORD physicalWriteCursorInBytes; 24914 DWORD availableBytesPlayback; 24915 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ 24916 24917 /* We need the physical play and write cursors. */ 24918 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { 24919 break; 24920 } 24921 24922 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { 24923 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; 24924 } 24925 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; 24926 24927 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ 24928 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { 24929 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ 24930 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { 24931 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; 24932 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ 24933 } else { 24934 /* This is an error. */ 24935 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); 24936 availableBytesPlayback = 0; 24937 } 24938 } else { 24939 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ 24940 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { 24941 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; 24942 } else { 24943 /* This is an error. */ 24944 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); 24945 availableBytesPlayback = 0; 24946 } 24947 } 24948 24949 /* If there's no room available for writing we need to wait for more. */ 24950 if (availableBytesPlayback == 0) { 24951 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ 24952 if (!isPlaybackDeviceStarted) { 24953 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); 24954 if (FAILED(hr)) { 24955 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); 24956 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); 24957 return ma_result_from_HRESULT(hr); 24958 } 24959 isPlaybackDeviceStarted = MA_TRUE; 24960 } else { 24961 ma_sleep(waitTimeInMilliseconds); 24962 continue; 24963 } 24964 } 24965 24966 24967 /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ 24968 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; 24969 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { 24970 /* Same loop iteration. Go up to the end of the buffer. */ 24971 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; 24972 } else { 24973 /* Different loop iterations. Go up to the physical play cursor. */ 24974 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; 24975 } 24976 24977 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); 24978 if (FAILED(hr)) { 24979 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); 24980 result = ma_result_from_HRESULT(hr); 24981 break; 24982 } 24983 24984 /* 24985 Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent 24986 endless glitching due to it constantly running out of data. 24987 */ 24988 if (isPlaybackDeviceStarted) { 24989 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; 24990 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { 24991 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; 24992 if (silentPaddingInBytes > lockSizeInBytesPlayback) { 24993 silentPaddingInBytes = lockSizeInBytesPlayback; 24994 } 24995 24996 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); 24997 } 24998 } 24999 25000 /* At this point we have a buffer for output. */ 25001 if (silentPaddingInBytes > 0) { 25002 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); 25003 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; 25004 } else { 25005 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); 25006 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; 25007 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); 25008 void* pConvertedFramesOut = pMappedDeviceBufferPlayback; 25009 25010 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); 25011 if (result != MA_SUCCESS) { 25012 break; 25013 } 25014 25015 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; 25016 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; 25017 } 25018 25019 25020 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); 25021 if (FAILED(hr)) { 25022 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); 25023 result = ma_result_from_HRESULT(hr); 25024 break; 25025 } 25026 25027 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; 25028 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { 25029 virtualWriteCursorInBytesPlayback = 0; 25030 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; 25031 } 25032 25033 /* 25034 We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds 25035 a bit of a buffer to prevent the playback buffer from getting starved. 25036 */ 25037 framesWrittenToPlaybackDevice += framesWrittenThisIteration; 25038 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { 25039 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); 25040 if (FAILED(hr)) { 25041 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); 25042 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); 25043 return ma_result_from_HRESULT(hr); 25044 } 25045 isPlaybackDeviceStarted = MA_TRUE; 25046 } 25047 25048 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { 25049 break; /* We're finished with the output data.*/ 25050 } 25051 } 25052 25053 if (clientCapturedFramesToProcess == 0) { 25054 break; /* We just consumed every input sample. */ 25055 } 25056 } 25057 25058 25059 /* At this point we're done with the mapped portion of the capture buffer. */ 25060 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); 25061 if (FAILED(hr)) { 25062 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); 25063 return ma_result_from_HRESULT(hr); 25064 } 25065 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); 25066 } break; 25067 25068 25069 25070 case ma_device_type_capture: 25071 { 25072 DWORD physicalCaptureCursorInBytes; 25073 DWORD physicalReadCursorInBytes; 25074 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); 25075 if (FAILED(hr)) { 25076 return MA_ERROR; 25077 } 25078 25079 /* If the previous capture position is the same as the current position we need to wait a bit longer. */ 25080 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { 25081 ma_sleep(waitTimeInMilliseconds); 25082 continue; 25083 } 25084 25085 /* Getting here means we have capture data available. */ 25086 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { 25087 /* The capture position has not looped. This is the simple case. */ 25088 lockOffsetInBytesCapture = prevReadCursorInBytesCapture; 25089 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); 25090 } else { 25091 /* 25092 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, 25093 do it again from the start. 25094 */ 25095 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { 25096 /* Lock up to the end of the buffer. */ 25097 lockOffsetInBytesCapture = prevReadCursorInBytesCapture; 25098 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; 25099 } else { 25100 /* Lock starting from the start of the buffer. */ 25101 lockOffsetInBytesCapture = 0; 25102 lockSizeInBytesCapture = physicalReadCursorInBytes; 25103 } 25104 } 25105 25106 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { 25107 ma_sleep(waitTimeInMilliseconds); 25108 continue; /* Nothing is available in the capture buffer. */ 25109 } 25110 25111 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); 25112 if (FAILED(hr)) { 25113 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); 25114 result = ma_result_from_HRESULT(hr); 25115 } 25116 25117 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { 25118 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); 25119 } 25120 25121 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); 25122 25123 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); 25124 if (FAILED(hr)) { 25125 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); 25126 return ma_result_from_HRESULT(hr); 25127 } 25128 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; 25129 25130 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { 25131 prevReadCursorInBytesCapture = 0; 25132 } 25133 } break; 25134 25135 25136 25137 case ma_device_type_playback: 25138 { 25139 DWORD availableBytesPlayback; 25140 DWORD physicalPlayCursorInBytes; 25141 DWORD physicalWriteCursorInBytes; 25142 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); 25143 if (FAILED(hr)) { 25144 break; 25145 } 25146 25147 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { 25148 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; 25149 } 25150 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; 25151 25152 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ 25153 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { 25154 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ 25155 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { 25156 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; 25157 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ 25158 } else { 25159 /* This is an error. */ 25160 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); 25161 availableBytesPlayback = 0; 25162 } 25163 } else { 25164 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ 25165 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { 25166 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; 25167 } else { 25168 /* This is an error. */ 25169 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); 25170 availableBytesPlayback = 0; 25171 } 25172 } 25173 25174 /* If there's no room available for writing we need to wait for more. */ 25175 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { 25176 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ 25177 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { 25178 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); 25179 if (FAILED(hr)) { 25180 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); 25181 return ma_result_from_HRESULT(hr); 25182 } 25183 isPlaybackDeviceStarted = MA_TRUE; 25184 } else { 25185 ma_sleep(waitTimeInMilliseconds); 25186 continue; 25187 } 25188 } 25189 25190 /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ 25191 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; 25192 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { 25193 /* Same loop iteration. Go up to the end of the buffer. */ 25194 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; 25195 } else { 25196 /* Different loop iterations. Go up to the physical play cursor. */ 25197 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; 25198 } 25199 25200 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); 25201 if (FAILED(hr)) { 25202 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); 25203 result = ma_result_from_HRESULT(hr); 25204 break; 25205 } 25206 25207 /* At this point we have a buffer for output. */ 25208 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); 25209 25210 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); 25211 if (FAILED(hr)) { 25212 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); 25213 result = ma_result_from_HRESULT(hr); 25214 break; 25215 } 25216 25217 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; 25218 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { 25219 virtualWriteCursorInBytesPlayback = 0; 25220 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; 25221 } 25222 25223 /* 25224 We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds 25225 a bit of a buffer to prevent the playback buffer from getting starved. 25226 */ 25227 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; 25228 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { 25229 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); 25230 if (FAILED(hr)) { 25231 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); 25232 return ma_result_from_HRESULT(hr); 25233 } 25234 isPlaybackDeviceStarted = MA_TRUE; 25235 } 25236 } break; 25237 25238 25239 default: return MA_INVALID_ARGS; /* Invalid device type. */ 25240 } 25241 25242 if (result != MA_SUCCESS) { 25243 return result; 25244 } 25245 } 25246 25247 /* Getting here means the device is being stopped. */ 25248 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 25249 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); 25250 if (FAILED(hr)) { 25251 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); 25252 return ma_result_from_HRESULT(hr); 25253 } 25254 } 25255 25256 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 25257 /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ 25258 if (isPlaybackDeviceStarted) { 25259 for (;;) { 25260 DWORD availableBytesPlayback = 0; 25261 DWORD physicalPlayCursorInBytes; 25262 DWORD physicalWriteCursorInBytes; 25263 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); 25264 if (FAILED(hr)) { 25265 break; 25266 } 25267 25268 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { 25269 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; 25270 } 25271 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; 25272 25273 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { 25274 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ 25275 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { 25276 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; 25277 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ 25278 } else { 25279 break; 25280 } 25281 } else { 25282 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ 25283 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { 25284 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; 25285 } else { 25286 break; 25287 } 25288 } 25289 25290 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { 25291 break; 25292 } 25293 25294 ma_sleep(waitTimeInMilliseconds); 25295 } 25296 } 25297 25298 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); 25299 if (FAILED(hr)) { 25300 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); 25301 return ma_result_from_HRESULT(hr); 25302 } 25303 25304 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); 25305 } 25306 25307 return MA_SUCCESS; 25308 } 25309 25310 static ma_result ma_context_uninit__dsound(ma_context* pContext) 25311 { 25312 MA_ASSERT(pContext != NULL); 25313 MA_ASSERT(pContext->backend == ma_backend_dsound); 25314 25315 ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); 25316 25317 return MA_SUCCESS; 25318 } 25319 25320 static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 25321 { 25322 MA_ASSERT(pContext != NULL); 25323 25324 (void)pConfig; 25325 25326 pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); 25327 if (pContext->dsound.hDSoundDLL == NULL) { 25328 return MA_API_NOT_FOUND; 25329 } 25330 25331 pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); 25332 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); 25333 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); 25334 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); 25335 25336 /* 25337 We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too 25338 well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient 25339 place to just disable the DirectSound backend for Windows 95. 25340 */ 25341 if (pContext->dsound.DirectSoundCreate == NULL || 25342 pContext->dsound.DirectSoundEnumerateA == NULL || 25343 pContext->dsound.DirectSoundCaptureCreate == NULL || 25344 pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { 25345 return MA_API_NOT_FOUND; 25346 } 25347 25348 pCallbacks->onContextInit = ma_context_init__dsound; 25349 pCallbacks->onContextUninit = ma_context_uninit__dsound; 25350 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; 25351 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; 25352 pCallbacks->onDeviceInit = ma_device_init__dsound; 25353 pCallbacks->onDeviceUninit = ma_device_uninit__dsound; 25354 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ 25355 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ 25356 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ 25357 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ 25358 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; 25359 25360 return MA_SUCCESS; 25361 } 25362 #endif 25363 25364 25365 25366 /****************************************************************************** 25367 25368 WinMM Backend 25369 25370 ******************************************************************************/ 25371 #ifdef MA_HAS_WINMM 25372 25373 /* 25374 Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN 25375 is defined. We need to define the types and functions we need manually. 25376 */ 25377 #define MA_MMSYSERR_NOERROR 0 25378 #define MA_MMSYSERR_ERROR 1 25379 #define MA_MMSYSERR_BADDEVICEID 2 25380 #define MA_MMSYSERR_INVALHANDLE 5 25381 #define MA_MMSYSERR_NOMEM 7 25382 #define MA_MMSYSERR_INVALFLAG 10 25383 #define MA_MMSYSERR_INVALPARAM 11 25384 #define MA_MMSYSERR_HANDLEBUSY 12 25385 25386 #define MA_CALLBACK_EVENT 0x00050000 25387 #define MA_WAVE_ALLOWSYNC 0x0002 25388 25389 #define MA_WHDR_DONE 0x00000001 25390 #define MA_WHDR_PREPARED 0x00000002 25391 #define MA_WHDR_BEGINLOOP 0x00000004 25392 #define MA_WHDR_ENDLOOP 0x00000008 25393 #define MA_WHDR_INQUEUE 0x00000010 25394 25395 #define MA_MAXPNAMELEN 32 25396 25397 typedef void* MA_HWAVEIN; 25398 typedef void* MA_HWAVEOUT; 25399 typedef UINT MA_MMRESULT; 25400 typedef UINT MA_MMVERSION; 25401 25402 typedef struct 25403 { 25404 WORD wMid; 25405 WORD wPid; 25406 MA_MMVERSION vDriverVersion; 25407 CHAR szPname[MA_MAXPNAMELEN]; 25408 DWORD dwFormats; 25409 WORD wChannels; 25410 WORD wReserved1; 25411 } MA_WAVEINCAPSA; 25412 25413 typedef struct 25414 { 25415 WORD wMid; 25416 WORD wPid; 25417 MA_MMVERSION vDriverVersion; 25418 CHAR szPname[MA_MAXPNAMELEN]; 25419 DWORD dwFormats; 25420 WORD wChannels; 25421 WORD wReserved1; 25422 DWORD dwSupport; 25423 } MA_WAVEOUTCAPSA; 25424 25425 typedef struct tagWAVEHDR 25426 { 25427 char* lpData; 25428 DWORD dwBufferLength; 25429 DWORD dwBytesRecorded; 25430 DWORD_PTR dwUser; 25431 DWORD dwFlags; 25432 DWORD dwLoops; 25433 struct tagWAVEHDR* lpNext; 25434 DWORD_PTR reserved; 25435 } MA_WAVEHDR; 25436 25437 typedef struct 25438 { 25439 WORD wMid; 25440 WORD wPid; 25441 MA_MMVERSION vDriverVersion; 25442 CHAR szPname[MA_MAXPNAMELEN]; 25443 DWORD dwFormats; 25444 WORD wChannels; 25445 WORD wReserved1; 25446 DWORD dwSupport; 25447 GUID ManufacturerGuid; 25448 GUID ProductGuid; 25449 GUID NameGuid; 25450 } MA_WAVEOUTCAPS2A; 25451 25452 typedef struct 25453 { 25454 WORD wMid; 25455 WORD wPid; 25456 MA_MMVERSION vDriverVersion; 25457 CHAR szPname[MA_MAXPNAMELEN]; 25458 DWORD dwFormats; 25459 WORD wChannels; 25460 WORD wReserved1; 25461 GUID ManufacturerGuid; 25462 GUID ProductGuid; 25463 GUID NameGuid; 25464 } MA_WAVEINCAPS2A; 25465 25466 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); 25467 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); 25468 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); 25469 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); 25470 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); 25471 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); 25472 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); 25473 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); 25474 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); 25475 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); 25476 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); 25477 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); 25478 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); 25479 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); 25480 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); 25481 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); 25482 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); 25483 25484 static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) 25485 { 25486 switch (resultMM) 25487 { 25488 case MA_MMSYSERR_NOERROR: return MA_SUCCESS; 25489 case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; 25490 case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; 25491 case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; 25492 case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; 25493 case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; 25494 case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; 25495 case MA_MMSYSERR_ERROR: return MA_ERROR; 25496 default: return MA_ERROR; 25497 } 25498 } 25499 25500 static char* ma_find_last_character(char* str, char ch) 25501 { 25502 char* last; 25503 25504 if (str == NULL) { 25505 return NULL; 25506 } 25507 25508 last = NULL; 25509 while (*str != '\0') { 25510 if (*str == ch) { 25511 last = str; 25512 } 25513 25514 str += 1; 25515 } 25516 25517 return last; 25518 } 25519 25520 static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) 25521 { 25522 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); 25523 } 25524 25525 25526 /* 25527 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so 25528 we can do things generically and typesafely. Names are being kept the same for consistency. 25529 */ 25530 typedef struct 25531 { 25532 CHAR szPname[MA_MAXPNAMELEN]; 25533 DWORD dwFormats; 25534 WORD wChannels; 25535 GUID NameGuid; 25536 } MA_WAVECAPSA; 25537 25538 static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) 25539 { 25540 WORD bitsPerSample = 0; 25541 DWORD sampleRate = 0; 25542 25543 if (pBitsPerSample) { 25544 *pBitsPerSample = 0; 25545 } 25546 if (pSampleRate) { 25547 *pSampleRate = 0; 25548 } 25549 25550 if (channels == 1) { 25551 bitsPerSample = 16; 25552 if ((dwFormats & WAVE_FORMAT_48M16) != 0) { 25553 sampleRate = 48000; 25554 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { 25555 sampleRate = 44100; 25556 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { 25557 sampleRate = 22050; 25558 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { 25559 sampleRate = 11025; 25560 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { 25561 sampleRate = 96000; 25562 } else { 25563 bitsPerSample = 8; 25564 if ((dwFormats & WAVE_FORMAT_48M08) != 0) { 25565 sampleRate = 48000; 25566 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { 25567 sampleRate = 44100; 25568 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { 25569 sampleRate = 22050; 25570 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { 25571 sampleRate = 11025; 25572 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { 25573 sampleRate = 96000; 25574 } else { 25575 return MA_FORMAT_NOT_SUPPORTED; 25576 } 25577 } 25578 } else { 25579 bitsPerSample = 16; 25580 if ((dwFormats & WAVE_FORMAT_48S16) != 0) { 25581 sampleRate = 48000; 25582 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { 25583 sampleRate = 44100; 25584 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { 25585 sampleRate = 22050; 25586 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { 25587 sampleRate = 11025; 25588 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { 25589 sampleRate = 96000; 25590 } else { 25591 bitsPerSample = 8; 25592 if ((dwFormats & WAVE_FORMAT_48S08) != 0) { 25593 sampleRate = 48000; 25594 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { 25595 sampleRate = 44100; 25596 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { 25597 sampleRate = 22050; 25598 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { 25599 sampleRate = 11025; 25600 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { 25601 sampleRate = 96000; 25602 } else { 25603 return MA_FORMAT_NOT_SUPPORTED; 25604 } 25605 } 25606 } 25607 25608 if (pBitsPerSample) { 25609 *pBitsPerSample = bitsPerSample; 25610 } 25611 if (pSampleRate) { 25612 *pSampleRate = sampleRate; 25613 } 25614 25615 return MA_SUCCESS; 25616 } 25617 25618 static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) 25619 { 25620 ma_result result; 25621 25622 MA_ASSERT(pWF != NULL); 25623 25624 MA_ZERO_OBJECT(pWF); 25625 pWF->cbSize = sizeof(*pWF); 25626 pWF->wFormatTag = WAVE_FORMAT_PCM; 25627 pWF->nChannels = (WORD)channels; 25628 if (pWF->nChannels > 2) { 25629 pWF->nChannels = 2; 25630 } 25631 25632 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); 25633 if (result != MA_SUCCESS) { 25634 return result; 25635 } 25636 25637 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); 25638 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; 25639 25640 return MA_SUCCESS; 25641 } 25642 25643 static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) 25644 { 25645 WORD bitsPerSample; 25646 DWORD sampleRate; 25647 ma_result result; 25648 25649 MA_ASSERT(pContext != NULL); 25650 MA_ASSERT(pCaps != NULL); 25651 MA_ASSERT(pDeviceInfo != NULL); 25652 25653 /* 25654 Name / Description 25655 25656 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking 25657 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try 25658 looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. 25659 */ 25660 25661 /* Set the default to begin with. */ 25662 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); 25663 25664 /* 25665 Now try the registry. There's a few things to consider here: 25666 - The name GUID can be null, in which we case we just need to stick to the original 31 characters. 25667 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. 25668 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The 25669 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"), 25670 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to 25671 usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component 25672 name, and then concatenate the name from the registry. 25673 */ 25674 if (!ma_is_guid_null(&pCaps->NameGuid)) { 25675 WCHAR guidStrW[256]; 25676 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { 25677 char guidStr[256]; 25678 char keyStr[1024]; 25679 HKEY hKey; 25680 25681 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); 25682 25683 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); 25684 ma_strcat_s(keyStr, sizeof(keyStr), guidStr); 25685 25686 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { 25687 BYTE nameFromReg[512]; 25688 DWORD nameFromRegSize = sizeof(nameFromReg); 25689 LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); 25690 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); 25691 25692 if (resultWin32 == ERROR_SUCCESS) { 25693 /* We have the value from the registry, so now we need to construct the name string. */ 25694 char name[1024]; 25695 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { 25696 char* nameBeg = ma_find_last_character(name, '('); 25697 if (nameBeg != NULL) { 25698 size_t leadingLen = (nameBeg - name); 25699 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); 25700 25701 /* The closing ")", if it can fit. */ 25702 if (leadingLen + nameFromRegSize < sizeof(name)-1) { 25703 ma_strcat_s(name, sizeof(name), ")"); 25704 } 25705 25706 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); 25707 } 25708 } 25709 } 25710 } 25711 } 25712 } 25713 25714 25715 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); 25716 if (result != MA_SUCCESS) { 25717 return result; 25718 } 25719 25720 if (bitsPerSample == 8) { 25721 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; 25722 } else if (bitsPerSample == 16) { 25723 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; 25724 } else if (bitsPerSample == 24) { 25725 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; 25726 } else if (bitsPerSample == 32) { 25727 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; 25728 } else { 25729 return MA_FORMAT_NOT_SUPPORTED; 25730 } 25731 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; 25732 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; 25733 pDeviceInfo->nativeDataFormats[0].flags = 0; 25734 pDeviceInfo->nativeDataFormatCount = 1; 25735 25736 return MA_SUCCESS; 25737 } 25738 25739 static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) 25740 { 25741 MA_WAVECAPSA caps; 25742 25743 MA_ASSERT(pContext != NULL); 25744 MA_ASSERT(pCaps != NULL); 25745 MA_ASSERT(pDeviceInfo != NULL); 25746 25747 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); 25748 caps.dwFormats = pCaps->dwFormats; 25749 caps.wChannels = pCaps->wChannels; 25750 caps.NameGuid = pCaps->NameGuid; 25751 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); 25752 } 25753 25754 static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) 25755 { 25756 MA_WAVECAPSA caps; 25757 25758 MA_ASSERT(pContext != NULL); 25759 MA_ASSERT(pCaps != NULL); 25760 MA_ASSERT(pDeviceInfo != NULL); 25761 25762 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); 25763 caps.dwFormats = pCaps->dwFormats; 25764 caps.wChannels = pCaps->wChannels; 25765 caps.NameGuid = pCaps->NameGuid; 25766 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); 25767 } 25768 25769 25770 static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 25771 { 25772 UINT playbackDeviceCount; 25773 UINT captureDeviceCount; 25774 UINT iPlaybackDevice; 25775 UINT iCaptureDevice; 25776 25777 MA_ASSERT(pContext != NULL); 25778 MA_ASSERT(callback != NULL); 25779 25780 /* Playback. */ 25781 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); 25782 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { 25783 MA_MMRESULT result; 25784 MA_WAVEOUTCAPS2A caps; 25785 25786 MA_ZERO_OBJECT(&caps); 25787 25788 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); 25789 if (result == MA_MMSYSERR_NOERROR) { 25790 ma_device_info deviceInfo; 25791 25792 MA_ZERO_OBJECT(&deviceInfo); 25793 deviceInfo.id.winmm = iPlaybackDevice; 25794 25795 /* The first enumerated device is the default device. */ 25796 if (iPlaybackDevice == 0) { 25797 deviceInfo.isDefault = MA_TRUE; 25798 } 25799 25800 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { 25801 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 25802 if (cbResult == MA_FALSE) { 25803 return MA_SUCCESS; /* Enumeration was stopped. */ 25804 } 25805 } 25806 } 25807 } 25808 25809 /* Capture. */ 25810 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); 25811 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { 25812 MA_MMRESULT result; 25813 MA_WAVEINCAPS2A caps; 25814 25815 MA_ZERO_OBJECT(&caps); 25816 25817 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); 25818 if (result == MA_MMSYSERR_NOERROR) { 25819 ma_device_info deviceInfo; 25820 25821 MA_ZERO_OBJECT(&deviceInfo); 25822 deviceInfo.id.winmm = iCaptureDevice; 25823 25824 /* The first enumerated device is the default device. */ 25825 if (iCaptureDevice == 0) { 25826 deviceInfo.isDefault = MA_TRUE; 25827 } 25828 25829 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { 25830 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 25831 if (cbResult == MA_FALSE) { 25832 return MA_SUCCESS; /* Enumeration was stopped. */ 25833 } 25834 } 25835 } 25836 } 25837 25838 return MA_SUCCESS; 25839 } 25840 25841 static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 25842 { 25843 UINT winMMDeviceID; 25844 25845 MA_ASSERT(pContext != NULL); 25846 25847 winMMDeviceID = 0; 25848 if (pDeviceID != NULL) { 25849 winMMDeviceID = (UINT)pDeviceID->winmm; 25850 } 25851 25852 pDeviceInfo->id.winmm = winMMDeviceID; 25853 25854 /* The first ID is the default device. */ 25855 if (winMMDeviceID == 0) { 25856 pDeviceInfo->isDefault = MA_TRUE; 25857 } 25858 25859 if (deviceType == ma_device_type_playback) { 25860 MA_MMRESULT result; 25861 MA_WAVEOUTCAPS2A caps; 25862 25863 MA_ZERO_OBJECT(&caps); 25864 25865 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); 25866 if (result == MA_MMSYSERR_NOERROR) { 25867 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); 25868 } 25869 } else { 25870 MA_MMRESULT result; 25871 MA_WAVEINCAPS2A caps; 25872 25873 MA_ZERO_OBJECT(&caps); 25874 25875 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); 25876 if (result == MA_MMSYSERR_NOERROR) { 25877 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); 25878 } 25879 } 25880 25881 return MA_NO_DEVICE; 25882 } 25883 25884 25885 static ma_result ma_device_uninit__winmm(ma_device* pDevice) 25886 { 25887 MA_ASSERT(pDevice != NULL); 25888 25889 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 25890 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); 25891 CloseHandle((HANDLE)pDevice->winmm.hEventCapture); 25892 } 25893 25894 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 25895 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); 25896 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); 25897 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); 25898 } 25899 25900 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); 25901 25902 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ 25903 25904 return MA_SUCCESS; 25905 } 25906 25907 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) 25908 { 25909 /* WinMM has a minimum period size of 40ms. */ 25910 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); 25911 ma_uint32 periodSizeInFrames; 25912 25913 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); 25914 if (periodSizeInFrames < minPeriodSizeInFrames) { 25915 periodSizeInFrames = minPeriodSizeInFrames; 25916 } 25917 25918 return periodSizeInFrames; 25919 } 25920 25921 static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 25922 { 25923 const char* errorMsg = ""; 25924 ma_result errorCode = MA_ERROR; 25925 ma_result result = MA_SUCCESS; 25926 ma_uint32 heapSize; 25927 UINT winMMDeviceIDPlayback = 0; 25928 UINT winMMDeviceIDCapture = 0; 25929 25930 MA_ASSERT(pDevice != NULL); 25931 25932 MA_ZERO_OBJECT(&pDevice->winmm); 25933 25934 if (pConfig->deviceType == ma_device_type_loopback) { 25935 return MA_DEVICE_TYPE_NOT_SUPPORTED; 25936 } 25937 25938 /* No exlusive mode with WinMM. */ 25939 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || 25940 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { 25941 return MA_SHARE_MODE_NOT_SUPPORTED; 25942 } 25943 25944 if (pDescriptorPlayback->pDeviceID != NULL) { 25945 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; 25946 } 25947 if (pDescriptorCapture->pDeviceID != NULL) { 25948 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; 25949 } 25950 25951 /* The capture device needs to be initialized first. */ 25952 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 25953 MA_WAVEINCAPSA caps; 25954 MA_WAVEFORMATEX wf; 25955 MA_MMRESULT resultMM; 25956 25957 /* We use an event to know when a new fragment needs to be enqueued. */ 25958 pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); 25959 if (pDevice->winmm.hEventCapture == NULL) { 25960 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); 25961 goto on_error; 25962 } 25963 25964 /* The format should be based on the device's actual format. */ 25965 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { 25966 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; 25967 goto on_error; 25968 } 25969 25970 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); 25971 if (result != MA_SUCCESS) { 25972 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; 25973 goto on_error; 25974 } 25975 25976 resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); 25977 if (resultMM != MA_MMSYSERR_NOERROR) { 25978 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; 25979 goto on_error; 25980 } 25981 25982 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); 25983 pDescriptorCapture->channels = wf.nChannels; 25984 pDescriptorCapture->sampleRate = wf.nSamplesPerSec; 25985 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); 25986 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; 25987 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); 25988 } 25989 25990 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 25991 MA_WAVEOUTCAPSA caps; 25992 MA_WAVEFORMATEX wf; 25993 MA_MMRESULT resultMM; 25994 25995 /* We use an event to know when a new fragment needs to be enqueued. */ 25996 pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); 25997 if (pDevice->winmm.hEventPlayback == NULL) { 25998 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); 25999 goto on_error; 26000 } 26001 26002 /* The format should be based on the device's actual format. */ 26003 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { 26004 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; 26005 goto on_error; 26006 } 26007 26008 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); 26009 if (result != MA_SUCCESS) { 26010 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; 26011 goto on_error; 26012 } 26013 26014 resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); 26015 if (resultMM != MA_MMSYSERR_NOERROR) { 26016 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; 26017 goto on_error; 26018 } 26019 26020 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); 26021 pDescriptorPlayback->channels = wf.nChannels; 26022 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; 26023 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); 26024 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; 26025 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); 26026 } 26027 26028 /* 26029 The heap allocated data is allocated like so: 26030 26031 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] 26032 */ 26033 heapSize = 0; 26034 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 26035 heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); 26036 } 26037 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 26038 heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); 26039 } 26040 26041 pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); 26042 if (pDevice->winmm._pHeapData == NULL) { 26043 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; 26044 goto on_error; 26045 } 26046 26047 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); 26048 26049 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 26050 ma_uint32 iPeriod; 26051 26052 if (pConfig->deviceType == ma_device_type_capture) { 26053 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; 26054 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); 26055 } else { 26056 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; 26057 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); 26058 } 26059 26060 /* Prepare headers. */ 26061 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { 26062 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); 26063 26064 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); 26065 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; 26066 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; 26067 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; 26068 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); 26069 26070 /* 26071 The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means 26072 it's unlocked and available for writing. A value of 1 means it's locked. 26073 */ 26074 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; 26075 } 26076 } 26077 26078 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 26079 ma_uint32 iPeriod; 26080 26081 if (pConfig->deviceType == ma_device_type_playback) { 26082 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; 26083 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); 26084 } else { 26085 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); 26086 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); 26087 } 26088 26089 /* Prepare headers. */ 26090 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { 26091 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); 26092 26093 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); 26094 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; 26095 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; 26096 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; 26097 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); 26098 26099 /* 26100 The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means 26101 it's unlocked and available for writing. A value of 1 means it's locked. 26102 */ 26103 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; 26104 } 26105 } 26106 26107 return MA_SUCCESS; 26108 26109 on_error: 26110 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 26111 if (pDevice->winmm.pWAVEHDRCapture != NULL) { 26112 ma_uint32 iPeriod; 26113 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { 26114 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); 26115 } 26116 } 26117 26118 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); 26119 } 26120 26121 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 26122 if (pDevice->winmm.pWAVEHDRCapture != NULL) { 26123 ma_uint32 iPeriod; 26124 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { 26125 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); 26126 } 26127 } 26128 26129 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); 26130 } 26131 26132 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); 26133 26134 if (errorMsg != NULL && errorMsg[0] != '\0') { 26135 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); 26136 } 26137 26138 return errorCode; 26139 } 26140 26141 static ma_result ma_device_start__winmm(ma_device* pDevice) 26142 { 26143 MA_ASSERT(pDevice != NULL); 26144 26145 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 26146 MA_MMRESULT resultMM; 26147 MA_WAVEHDR* pWAVEHDR; 26148 ma_uint32 iPeriod; 26149 26150 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; 26151 26152 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ 26153 ResetEvent((HANDLE)pDevice->winmm.hEventCapture); 26154 26155 /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ 26156 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { 26157 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); 26158 if (resultMM != MA_MMSYSERR_NOERROR) { 26159 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); 26160 return ma_result_from_MMRESULT(resultMM); 26161 } 26162 26163 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ 26164 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ 26165 } 26166 26167 /* Capture devices need to be explicitly started, unlike playback devices. */ 26168 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); 26169 if (resultMM != MA_MMSYSERR_NOERROR) { 26170 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); 26171 return ma_result_from_MMRESULT(resultMM); 26172 } 26173 } 26174 26175 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 26176 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ 26177 } 26178 26179 return MA_SUCCESS; 26180 } 26181 26182 static ma_result ma_device_stop__winmm(ma_device* pDevice) 26183 { 26184 MA_MMRESULT resultMM; 26185 26186 MA_ASSERT(pDevice != NULL); 26187 26188 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 26189 if (pDevice->winmm.hDeviceCapture == NULL) { 26190 return MA_INVALID_ARGS; 26191 } 26192 26193 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); 26194 if (resultMM != MA_MMSYSERR_NOERROR) { 26195 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); 26196 } 26197 } 26198 26199 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 26200 ma_uint32 iPeriod; 26201 MA_WAVEHDR* pWAVEHDR; 26202 26203 if (pDevice->winmm.hDevicePlayback == NULL) { 26204 return MA_INVALID_ARGS; 26205 } 26206 26207 /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ 26208 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; 26209 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { 26210 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ 26211 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { 26212 break; /* An error occurred so just abandon ship and stop the device without draining. */ 26213 } 26214 26215 pWAVEHDR[iPeriod].dwUser = 0; 26216 } 26217 } 26218 26219 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); 26220 if (resultMM != MA_MMSYSERR_NOERROR) { 26221 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); 26222 } 26223 } 26224 26225 return MA_SUCCESS; 26226 } 26227 26228 static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 26229 { 26230 ma_result result = MA_SUCCESS; 26231 MA_MMRESULT resultMM; 26232 ma_uint32 totalFramesWritten; 26233 MA_WAVEHDR* pWAVEHDR; 26234 26235 MA_ASSERT(pDevice != NULL); 26236 MA_ASSERT(pPCMFrames != NULL); 26237 26238 if (pFramesWritten != NULL) { 26239 *pFramesWritten = 0; 26240 } 26241 26242 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; 26243 26244 /* Keep processing as much data as possible. */ 26245 totalFramesWritten = 0; 26246 while (totalFramesWritten < frameCount) { 26247 /* If the current header has some space available we need to write part of it. */ 26248 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ 26249 /* 26250 This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to 26251 write it out and move on to the next iteration. 26252 */ 26253 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 26254 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; 26255 26256 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); 26257 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); 26258 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); 26259 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); 26260 26261 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; 26262 totalFramesWritten += framesToCopy; 26263 26264 /* If we've consumed the buffer entirely we need to write it out to the device. */ 26265 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { 26266 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ 26267 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ 26268 26269 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ 26270 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); 26271 26272 /* The device will be started here. */ 26273 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); 26274 if (resultMM != MA_MMSYSERR_NOERROR) { 26275 result = ma_result_from_MMRESULT(resultMM); 26276 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); 26277 break; 26278 } 26279 26280 /* Make sure we move to the next header. */ 26281 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; 26282 pDevice->winmm.headerFramesConsumedPlayback = 0; 26283 } 26284 26285 /* If at this point we have consumed the entire input buffer we can return. */ 26286 MA_ASSERT(totalFramesWritten <= frameCount); 26287 if (totalFramesWritten == frameCount) { 26288 break; 26289 } 26290 26291 /* Getting here means there's more to process. */ 26292 continue; 26293 } 26294 26295 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ 26296 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { 26297 result = MA_ERROR; 26298 break; 26299 } 26300 26301 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ 26302 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { 26303 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ 26304 pDevice->winmm.headerFramesConsumedPlayback = 0; 26305 } 26306 26307 /* If the device has been stopped we need to break. */ 26308 if (ma_device_get_state(pDevice) != ma_device_state_started) { 26309 break; 26310 } 26311 } 26312 26313 if (pFramesWritten != NULL) { 26314 *pFramesWritten = totalFramesWritten; 26315 } 26316 26317 return result; 26318 } 26319 26320 static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 26321 { 26322 ma_result result = MA_SUCCESS; 26323 MA_MMRESULT resultMM; 26324 ma_uint32 totalFramesRead; 26325 MA_WAVEHDR* pWAVEHDR; 26326 26327 MA_ASSERT(pDevice != NULL); 26328 MA_ASSERT(pPCMFrames != NULL); 26329 26330 if (pFramesRead != NULL) { 26331 *pFramesRead = 0; 26332 } 26333 26334 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; 26335 26336 /* Keep processing as much data as possible. */ 26337 totalFramesRead = 0; 26338 while (totalFramesRead < frameCount) { 26339 /* If the current header has some space available we need to write part of it. */ 26340 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ 26341 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ 26342 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 26343 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; 26344 26345 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); 26346 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); 26347 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); 26348 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); 26349 26350 pDevice->winmm.headerFramesConsumedCapture += framesToCopy; 26351 totalFramesRead += framesToCopy; 26352 26353 /* If we've consumed the buffer entirely we need to add it back to the device. */ 26354 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { 26355 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ 26356 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ 26357 26358 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ 26359 ResetEvent((HANDLE)pDevice->winmm.hEventCapture); 26360 26361 /* The device will be started here. */ 26362 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); 26363 if (resultMM != MA_MMSYSERR_NOERROR) { 26364 result = ma_result_from_MMRESULT(resultMM); 26365 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); 26366 break; 26367 } 26368 26369 /* Make sure we move to the next header. */ 26370 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; 26371 pDevice->winmm.headerFramesConsumedCapture = 0; 26372 } 26373 26374 /* If at this point we have filled the entire input buffer we can return. */ 26375 MA_ASSERT(totalFramesRead <= frameCount); 26376 if (totalFramesRead == frameCount) { 26377 break; 26378 } 26379 26380 /* Getting here means there's more to process. */ 26381 continue; 26382 } 26383 26384 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ 26385 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { 26386 result = MA_ERROR; 26387 break; 26388 } 26389 26390 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ 26391 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { 26392 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ 26393 pDevice->winmm.headerFramesConsumedCapture = 0; 26394 } 26395 26396 /* If the device has been stopped we need to break. */ 26397 if (ma_device_get_state(pDevice) != ma_device_state_started) { 26398 break; 26399 } 26400 } 26401 26402 if (pFramesRead != NULL) { 26403 *pFramesRead = totalFramesRead; 26404 } 26405 26406 return result; 26407 } 26408 26409 static ma_result ma_context_uninit__winmm(ma_context* pContext) 26410 { 26411 MA_ASSERT(pContext != NULL); 26412 MA_ASSERT(pContext->backend == ma_backend_winmm); 26413 26414 ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); 26415 return MA_SUCCESS; 26416 } 26417 26418 static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 26419 { 26420 MA_ASSERT(pContext != NULL); 26421 26422 (void)pConfig; 26423 26424 pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); 26425 if (pContext->winmm.hWinMM == NULL) { 26426 return MA_NO_BACKEND; 26427 } 26428 26429 pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); 26430 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); 26431 pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); 26432 pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); 26433 pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); 26434 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); 26435 pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); 26436 pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); 26437 pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); 26438 pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); 26439 pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); 26440 pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); 26441 pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); 26442 pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); 26443 pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); 26444 pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); 26445 pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); 26446 26447 pCallbacks->onContextInit = ma_context_init__winmm; 26448 pCallbacks->onContextUninit = ma_context_uninit__winmm; 26449 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; 26450 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; 26451 pCallbacks->onDeviceInit = ma_device_init__winmm; 26452 pCallbacks->onDeviceUninit = ma_device_uninit__winmm; 26453 pCallbacks->onDeviceStart = ma_device_start__winmm; 26454 pCallbacks->onDeviceStop = ma_device_stop__winmm; 26455 pCallbacks->onDeviceRead = ma_device_read__winmm; 26456 pCallbacks->onDeviceWrite = ma_device_write__winmm; 26457 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ 26458 26459 return MA_SUCCESS; 26460 } 26461 #endif 26462 26463 26464 26465 26466 /****************************************************************************** 26467 26468 ALSA Backend 26469 26470 ******************************************************************************/ 26471 #ifdef MA_HAS_ALSA 26472 26473 #include <poll.h> /* poll(), struct pollfd */ 26474 #include <sys/eventfd.h> /* eventfd() */ 26475 26476 #ifdef MA_NO_RUNTIME_LINKING 26477 26478 /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ 26479 #if !defined(__cplusplus) 26480 #if defined(__STRICT_ANSI__) 26481 #if !defined(inline) 26482 #define inline __inline__ __attribute__((always_inline)) 26483 #define MA_INLINE_DEFINED 26484 #endif 26485 #endif 26486 #endif 26487 #include <alsa/asoundlib.h> 26488 #if defined(MA_INLINE_DEFINED) 26489 #undef inline 26490 #undef MA_INLINE_DEFINED 26491 #endif 26492 26493 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; 26494 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; 26495 typedef snd_pcm_stream_t ma_snd_pcm_stream_t; 26496 typedef snd_pcm_format_t ma_snd_pcm_format_t; 26497 typedef snd_pcm_access_t ma_snd_pcm_access_t; 26498 typedef snd_pcm_t ma_snd_pcm_t; 26499 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; 26500 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; 26501 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; 26502 typedef snd_pcm_info_t ma_snd_pcm_info_t; 26503 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; 26504 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; 26505 typedef snd_pcm_state_t ma_snd_pcm_state_t; 26506 26507 /* snd_pcm_stream_t */ 26508 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK 26509 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE 26510 26511 /* snd_pcm_format_t */ 26512 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN 26513 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 26514 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE 26515 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE 26516 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE 26517 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE 26518 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE 26519 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE 26520 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE 26521 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE 26522 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE 26523 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE 26524 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW 26525 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW 26526 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE 26527 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE 26528 26529 /* ma_snd_pcm_access_t */ 26530 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED 26531 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED 26532 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX 26533 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED 26534 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED 26535 26536 /* Channel positions. */ 26537 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN 26538 #define MA_SND_CHMAP_NA SND_CHMAP_NA 26539 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO 26540 #define MA_SND_CHMAP_FL SND_CHMAP_FL 26541 #define MA_SND_CHMAP_FR SND_CHMAP_FR 26542 #define MA_SND_CHMAP_RL SND_CHMAP_RL 26543 #define MA_SND_CHMAP_RR SND_CHMAP_RR 26544 #define MA_SND_CHMAP_FC SND_CHMAP_FC 26545 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE 26546 #define MA_SND_CHMAP_SL SND_CHMAP_SL 26547 #define MA_SND_CHMAP_SR SND_CHMAP_SR 26548 #define MA_SND_CHMAP_RC SND_CHMAP_RC 26549 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC 26550 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC 26551 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC 26552 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC 26553 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW 26554 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW 26555 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH 26556 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH 26557 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH 26558 #define MA_SND_CHMAP_TC SND_CHMAP_TC 26559 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL 26560 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR 26561 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC 26562 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL 26563 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR 26564 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC 26565 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC 26566 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC 26567 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL 26568 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR 26569 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE 26570 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE 26571 #define MA_SND_CHMAP_BC SND_CHMAP_BC 26572 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC 26573 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC 26574 26575 /* Open mode flags. */ 26576 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE 26577 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS 26578 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT 26579 #else 26580 #include <errno.h> /* For EPIPE, etc. */ 26581 typedef unsigned long ma_snd_pcm_uframes_t; 26582 typedef long ma_snd_pcm_sframes_t; 26583 typedef int ma_snd_pcm_stream_t; 26584 typedef int ma_snd_pcm_format_t; 26585 typedef int ma_snd_pcm_access_t; 26586 typedef int ma_snd_pcm_state_t; 26587 typedef struct ma_snd_pcm_t ma_snd_pcm_t; 26588 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; 26589 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; 26590 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; 26591 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; 26592 typedef struct 26593 { 26594 void* addr; 26595 unsigned int first; 26596 unsigned int step; 26597 } ma_snd_pcm_channel_area_t; 26598 typedef struct 26599 { 26600 unsigned int channels; 26601 unsigned int pos[1]; 26602 } ma_snd_pcm_chmap_t; 26603 26604 /* snd_pcm_state_t */ 26605 #define MA_SND_PCM_STATE_OPEN 0 26606 #define MA_SND_PCM_STATE_SETUP 1 26607 #define MA_SND_PCM_STATE_PREPARED 2 26608 #define MA_SND_PCM_STATE_RUNNING 3 26609 #define MA_SND_PCM_STATE_XRUN 4 26610 #define MA_SND_PCM_STATE_DRAINING 5 26611 #define MA_SND_PCM_STATE_PAUSED 6 26612 #define MA_SND_PCM_STATE_SUSPENDED 7 26613 #define MA_SND_PCM_STATE_DISCONNECTED 8 26614 26615 /* snd_pcm_stream_t */ 26616 #define MA_SND_PCM_STREAM_PLAYBACK 0 26617 #define MA_SND_PCM_STREAM_CAPTURE 1 26618 26619 /* snd_pcm_format_t */ 26620 #define MA_SND_PCM_FORMAT_UNKNOWN -1 26621 #define MA_SND_PCM_FORMAT_U8 1 26622 #define MA_SND_PCM_FORMAT_S16_LE 2 26623 #define MA_SND_PCM_FORMAT_S16_BE 3 26624 #define MA_SND_PCM_FORMAT_S24_LE 6 26625 #define MA_SND_PCM_FORMAT_S24_BE 7 26626 #define MA_SND_PCM_FORMAT_S32_LE 10 26627 #define MA_SND_PCM_FORMAT_S32_BE 11 26628 #define MA_SND_PCM_FORMAT_FLOAT_LE 14 26629 #define MA_SND_PCM_FORMAT_FLOAT_BE 15 26630 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16 26631 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17 26632 #define MA_SND_PCM_FORMAT_MU_LAW 20 26633 #define MA_SND_PCM_FORMAT_A_LAW 21 26634 #define MA_SND_PCM_FORMAT_S24_3LE 32 26635 #define MA_SND_PCM_FORMAT_S24_3BE 33 26636 26637 /* snd_pcm_access_t */ 26638 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 26639 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 26640 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 26641 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 26642 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 26643 26644 /* Channel positions. */ 26645 #define MA_SND_CHMAP_UNKNOWN 0 26646 #define MA_SND_CHMAP_NA 1 26647 #define MA_SND_CHMAP_MONO 2 26648 #define MA_SND_CHMAP_FL 3 26649 #define MA_SND_CHMAP_FR 4 26650 #define MA_SND_CHMAP_RL 5 26651 #define MA_SND_CHMAP_RR 6 26652 #define MA_SND_CHMAP_FC 7 26653 #define MA_SND_CHMAP_LFE 8 26654 #define MA_SND_CHMAP_SL 9 26655 #define MA_SND_CHMAP_SR 10 26656 #define MA_SND_CHMAP_RC 11 26657 #define MA_SND_CHMAP_FLC 12 26658 #define MA_SND_CHMAP_FRC 13 26659 #define MA_SND_CHMAP_RLC 14 26660 #define MA_SND_CHMAP_RRC 15 26661 #define MA_SND_CHMAP_FLW 16 26662 #define MA_SND_CHMAP_FRW 17 26663 #define MA_SND_CHMAP_FLH 18 26664 #define MA_SND_CHMAP_FCH 19 26665 #define MA_SND_CHMAP_FRH 20 26666 #define MA_SND_CHMAP_TC 21 26667 #define MA_SND_CHMAP_TFL 22 26668 #define MA_SND_CHMAP_TFR 23 26669 #define MA_SND_CHMAP_TFC 24 26670 #define MA_SND_CHMAP_TRL 25 26671 #define MA_SND_CHMAP_TRR 26 26672 #define MA_SND_CHMAP_TRC 27 26673 #define MA_SND_CHMAP_TFLC 28 26674 #define MA_SND_CHMAP_TFRC 29 26675 #define MA_SND_CHMAP_TSL 30 26676 #define MA_SND_CHMAP_TSR 31 26677 #define MA_SND_CHMAP_LLFE 32 26678 #define MA_SND_CHMAP_RLFE 33 26679 #define MA_SND_CHMAP_BC 34 26680 #define MA_SND_CHMAP_BLC 35 26681 #define MA_SND_CHMAP_BRC 36 26682 26683 /* Open mode flags. */ 26684 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 26685 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 26686 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 26687 #endif 26688 26689 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); 26690 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); 26691 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); 26692 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); 26693 typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); 26694 typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); 26695 typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); 26696 typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); 26697 typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); 26698 typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); 26699 typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); 26700 typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); 26701 typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); 26702 typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); 26703 typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); 26704 typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); 26705 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); 26706 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); 26707 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); 26708 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); 26709 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); 26710 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); 26711 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); 26712 typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); 26713 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); 26714 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); 26715 typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); 26716 typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); 26717 typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); 26718 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); 26719 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); 26720 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); 26721 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); 26722 typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); 26723 typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); 26724 typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); 26725 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); 26726 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); 26727 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); 26728 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); 26729 typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); 26730 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); 26731 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); 26732 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); 26733 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); 26734 typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); 26735 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); 26736 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); 26737 typedef int (* ma_snd_card_get_index_proc) (const char *name); 26738 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); 26739 typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); 26740 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); 26741 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); 26742 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); 26743 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); 26744 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); 26745 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); 26746 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); 26747 typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); 26748 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); 26749 typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); 26750 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); 26751 typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); 26752 typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); 26753 typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); 26754 typedef int (* ma_snd_config_update_free_global_proc) (void); 26755 26756 /* This array specifies each of the common devices that can be used for both playback and capture. */ 26757 static const char* g_maCommonDeviceNamesALSA[] = { 26758 "default", 26759 "null", 26760 "pulse", 26761 "jack" 26762 }; 26763 26764 /* This array allows us to blacklist specific playback devices. */ 26765 static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { 26766 "" 26767 }; 26768 26769 /* This array allows us to blacklist specific capture devices. */ 26770 static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { 26771 "" 26772 }; 26773 26774 26775 static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) 26776 { 26777 ma_snd_pcm_format_t ALSAFormats[] = { 26778 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ 26779 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ 26780 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ 26781 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ 26782 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ 26783 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ 26784 }; 26785 26786 if (ma_is_big_endian()) { 26787 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; 26788 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; 26789 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; 26790 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; 26791 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; 26792 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; 26793 } 26794 26795 return ALSAFormats[format]; 26796 } 26797 26798 static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) 26799 { 26800 if (ma_is_little_endian()) { 26801 switch (formatALSA) { 26802 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; 26803 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; 26804 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; 26805 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; 26806 default: break; 26807 } 26808 } else { 26809 switch (formatALSA) { 26810 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; 26811 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; 26812 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; 26813 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; 26814 default: break; 26815 } 26816 } 26817 26818 /* Endian agnostic. */ 26819 switch (formatALSA) { 26820 case MA_SND_PCM_FORMAT_U8: return ma_format_u8; 26821 default: return ma_format_unknown; 26822 } 26823 } 26824 26825 static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) 26826 { 26827 switch (alsaChannelPos) 26828 { 26829 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; 26830 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; 26831 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; 26832 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; 26833 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; 26834 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; 26835 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; 26836 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; 26837 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; 26838 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; 26839 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; 26840 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; 26841 case MA_SND_CHMAP_RLC: return 0; 26842 case MA_SND_CHMAP_RRC: return 0; 26843 case MA_SND_CHMAP_FLW: return 0; 26844 case MA_SND_CHMAP_FRW: return 0; 26845 case MA_SND_CHMAP_FLH: return 0; 26846 case MA_SND_CHMAP_FCH: return 0; 26847 case MA_SND_CHMAP_FRH: return 0; 26848 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; 26849 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; 26850 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; 26851 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; 26852 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; 26853 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; 26854 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; 26855 default: break; 26856 } 26857 26858 return 0; 26859 } 26860 26861 static ma_bool32 ma_is_common_device_name__alsa(const char* name) 26862 { 26863 size_t iName; 26864 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { 26865 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { 26866 return MA_TRUE; 26867 } 26868 } 26869 26870 return MA_FALSE; 26871 } 26872 26873 26874 static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) 26875 { 26876 size_t iName; 26877 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { 26878 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { 26879 return MA_TRUE; 26880 } 26881 } 26882 26883 return MA_FALSE; 26884 } 26885 26886 static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) 26887 { 26888 size_t iName; 26889 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { 26890 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { 26891 return MA_TRUE; 26892 } 26893 } 26894 26895 return MA_FALSE; 26896 } 26897 26898 static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) 26899 { 26900 if (deviceType == ma_device_type_playback) { 26901 return ma_is_playback_device_blacklisted__alsa(name); 26902 } else { 26903 return ma_is_capture_device_blacklisted__alsa(name); 26904 } 26905 } 26906 26907 26908 static const char* ma_find_char(const char* str, char c, int* index) 26909 { 26910 int i = 0; 26911 for (;;) { 26912 if (str[i] == '\0') { 26913 if (index) *index = -1; 26914 return NULL; 26915 } 26916 26917 if (str[i] == c) { 26918 if (index) *index = i; 26919 return str + i; 26920 } 26921 26922 i += 1; 26923 } 26924 26925 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ 26926 if (index) *index = -1; 26927 return NULL; 26928 } 26929 26930 static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) 26931 { 26932 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ 26933 26934 int commaPos; 26935 const char* dev; 26936 int i; 26937 26938 if (hwid == NULL) { 26939 return MA_FALSE; 26940 } 26941 26942 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { 26943 return MA_FALSE; 26944 } 26945 26946 hwid += 3; 26947 26948 dev = ma_find_char(hwid, ',', &commaPos); 26949 if (dev == NULL) { 26950 return MA_FALSE; 26951 } else { 26952 dev += 1; /* Skip past the ",". */ 26953 } 26954 26955 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ 26956 for (i = 0; i < commaPos; ++i) { 26957 if (hwid[i] < '0' || hwid[i] > '9') { 26958 return MA_FALSE; 26959 } 26960 } 26961 26962 /* Check if everything after the "," is numeric. If not, return false. */ 26963 i = 0; 26964 while (dev[i] != '\0') { 26965 if (dev[i] < '0' || dev[i] > '9') { 26966 return MA_FALSE; 26967 } 26968 i += 1; 26969 } 26970 26971 return MA_TRUE; 26972 } 26973 26974 static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ 26975 { 26976 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ 26977 26978 int colonPos; 26979 int commaPos; 26980 char card[256]; 26981 const char* dev; 26982 int cardIndex; 26983 26984 if (dst == NULL) { 26985 return -1; 26986 } 26987 if (dstSize < 7) { 26988 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ 26989 } 26990 26991 *dst = '\0'; /* Safety. */ 26992 if (src == NULL) { 26993 return -1; 26994 } 26995 26996 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ 26997 if (ma_is_device_name_in_hw_format__alsa(src)) { 26998 return ma_strcpy_s(dst, dstSize, src); 26999 } 27000 27001 src = ma_find_char(src, ':', &colonPos); 27002 if (src == NULL) { 27003 return -1; /* Couldn't find a colon */ 27004 } 27005 27006 dev = ma_find_char(src, ',', &commaPos); 27007 if (dev == NULL) { 27008 dev = "0"; 27009 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ 27010 } else { 27011 dev = dev + 5; /* +5 = ",DEV=" */ 27012 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ 27013 } 27014 27015 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); 27016 if (cardIndex < 0) { 27017 return -2; /* Failed to retrieve the card index. */ 27018 } 27019 27020 27021 /* Construction. */ 27022 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; 27023 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { 27024 return -3; 27025 } 27026 if (ma_strcat_s(dst, dstSize, ",") != 0) { 27027 return -3; 27028 } 27029 if (ma_strcat_s(dst, dstSize, dev) != 0) { 27030 return -3; 27031 } 27032 27033 return 0; 27034 } 27035 27036 static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) 27037 { 27038 ma_uint32 i; 27039 27040 MA_ASSERT(pHWID != NULL); 27041 27042 for (i = 0; i < count; ++i) { 27043 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { 27044 return MA_TRUE; 27045 } 27046 } 27047 27048 return MA_FALSE; 27049 } 27050 27051 27052 static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) 27053 { 27054 ma_snd_pcm_t* pPCM; 27055 ma_snd_pcm_stream_t stream; 27056 27057 MA_ASSERT(pContext != NULL); 27058 MA_ASSERT(ppPCM != NULL); 27059 27060 *ppPCM = NULL; 27061 pPCM = NULL; 27062 27063 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; 27064 27065 if (pDeviceID == NULL) { 27066 ma_bool32 isDeviceOpen; 27067 size_t i; 27068 27069 /* 27070 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes 27071 me feel better to try as hard as we can get to get _something_ working. 27072 */ 27073 const char* defaultDeviceNames[] = { 27074 "default", 27075 NULL, 27076 NULL, 27077 NULL, 27078 NULL, 27079 NULL, 27080 NULL 27081 }; 27082 27083 if (shareMode == ma_share_mode_exclusive) { 27084 defaultDeviceNames[1] = "hw"; 27085 defaultDeviceNames[2] = "hw:0"; 27086 defaultDeviceNames[3] = "hw:0,0"; 27087 } else { 27088 if (deviceType == ma_device_type_playback) { 27089 defaultDeviceNames[1] = "dmix"; 27090 defaultDeviceNames[2] = "dmix:0"; 27091 defaultDeviceNames[3] = "dmix:0,0"; 27092 } else { 27093 defaultDeviceNames[1] = "dsnoop"; 27094 defaultDeviceNames[2] = "dsnoop:0"; 27095 defaultDeviceNames[3] = "dsnoop:0,0"; 27096 } 27097 defaultDeviceNames[4] = "hw"; 27098 defaultDeviceNames[5] = "hw:0"; 27099 defaultDeviceNames[6] = "hw:0,0"; 27100 } 27101 27102 isDeviceOpen = MA_FALSE; 27103 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { 27104 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { 27105 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { 27106 isDeviceOpen = MA_TRUE; 27107 break; 27108 } 27109 } 27110 } 27111 27112 if (!isDeviceOpen) { 27113 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); 27114 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 27115 } 27116 } else { 27117 /* 27118 We're trying to open a specific device. There's a few things to consider here: 27119 27120 miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When 27121 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it 27122 finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). 27123 */ 27124 27125 /* May end up needing to make small adjustments to the ID, so make a copy. */ 27126 ma_device_id deviceID = *pDeviceID; 27127 int resultALSA = -ENODEV; 27128 27129 if (deviceID.alsa[0] != ':') { 27130 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ 27131 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); 27132 } else { 27133 char hwid[256]; 27134 27135 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ 27136 if (deviceID.alsa[1] == '\0') { 27137 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ 27138 } 27139 27140 if (shareMode == ma_share_mode_shared) { 27141 if (deviceType == ma_device_type_playback) { 27142 ma_strcpy_s(hwid, sizeof(hwid), "dmix"); 27143 } else { 27144 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); 27145 } 27146 27147 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { 27148 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); 27149 } 27150 } 27151 27152 /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ 27153 if (resultALSA != 0) { 27154 ma_strcpy_s(hwid, sizeof(hwid), "hw"); 27155 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { 27156 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); 27157 } 27158 } 27159 } 27160 27161 if (resultALSA < 0) { 27162 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); 27163 return ma_result_from_errno(-resultALSA); 27164 } 27165 } 27166 27167 *ppPCM = pPCM; 27168 return MA_SUCCESS; 27169 } 27170 27171 27172 static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 27173 { 27174 int resultALSA; 27175 ma_bool32 cbResult = MA_TRUE; 27176 char** ppDeviceHints; 27177 ma_device_id* pUniqueIDs = NULL; 27178 ma_uint32 uniqueIDCount = 0; 27179 char** ppNextDeviceHint; 27180 27181 MA_ASSERT(pContext != NULL); 27182 MA_ASSERT(callback != NULL); 27183 27184 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); 27185 27186 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); 27187 if (resultALSA < 0) { 27188 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); 27189 return ma_result_from_errno(-resultALSA); 27190 } 27191 27192 ppNextDeviceHint = ppDeviceHints; 27193 while (*ppNextDeviceHint != NULL) { 27194 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); 27195 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); 27196 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); 27197 ma_device_type deviceType = ma_device_type_playback; 27198 ma_bool32 stopEnumeration = MA_FALSE; 27199 char hwid[sizeof(pUniqueIDs->alsa)]; 27200 ma_device_info deviceInfo; 27201 27202 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { 27203 deviceType = ma_device_type_playback; 27204 } 27205 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { 27206 deviceType = ma_device_type_capture; 27207 } 27208 27209 if (NAME != NULL) { 27210 if (pContext->alsa.useVerboseDeviceEnumeration) { 27211 /* Verbose mode. Use the name exactly as-is. */ 27212 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); 27213 } else { 27214 /* Simplified mode. Use ":%d,%d" format. */ 27215 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { 27216 /* 27217 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the 27218 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device 27219 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the 27220 device type and sharing mode. 27221 */ 27222 char* dst = hwid; 27223 char* src = hwid+2; 27224 while ((*dst++ = *src++)); 27225 } else { 27226 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ 27227 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); 27228 } 27229 27230 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { 27231 goto next_device; /* The device has already been enumerated. Move on to the next one. */ 27232 } else { 27233 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ 27234 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); 27235 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); 27236 if (pNewUniqueIDs == NULL) { 27237 goto next_device; /* Failed to allocate memory. */ 27238 } 27239 27240 pUniqueIDs = pNewUniqueIDs; 27241 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); 27242 uniqueIDCount += 1; 27243 } 27244 } 27245 } else { 27246 MA_ZERO_MEMORY(hwid, sizeof(hwid)); 27247 } 27248 27249 MA_ZERO_OBJECT(&deviceInfo); 27250 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); 27251 27252 /* 27253 There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and 27254 just use the name of "default" as the indicator. 27255 */ 27256 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { 27257 deviceInfo.isDefault = MA_TRUE; 27258 } 27259 27260 27261 /* 27262 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose 27263 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish 27264 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the 27265 description. 27266 27267 The value in DESC seems to be split into two lines, with the first line being the name of the device and the 27268 second line being a description of the device. I don't like having the description be across two lines because 27269 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line 27270 being put into parentheses. In simplified mode I'm just stripping the second line entirely. 27271 */ 27272 if (DESC != NULL) { 27273 int lfPos; 27274 const char* line2 = ma_find_char(DESC, '\n', &lfPos); 27275 if (line2 != NULL) { 27276 line2 += 1; /* Skip past the new-line character. */ 27277 27278 if (pContext->alsa.useVerboseDeviceEnumeration) { 27279 /* Verbose mode. Put the second line in brackets. */ 27280 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); 27281 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); 27282 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); 27283 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); 27284 } else { 27285 /* Simplified mode. Strip the second line entirely. */ 27286 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); 27287 } 27288 } else { 27289 /* There's no second line. Just copy the whole description. */ 27290 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); 27291 } 27292 } 27293 27294 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { 27295 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); 27296 } 27297 27298 /* 27299 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback 27300 again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which 27301 means both Input and Output. 27302 */ 27303 if (cbResult) { 27304 if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { 27305 if (deviceType == ma_device_type_playback) { 27306 if (!ma_is_capture_device_blacklisted__alsa(NAME)) { 27307 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 27308 } 27309 } else { 27310 if (!ma_is_playback_device_blacklisted__alsa(NAME)) { 27311 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 27312 } 27313 } 27314 } 27315 } 27316 27317 if (cbResult == MA_FALSE) { 27318 stopEnumeration = MA_TRUE; 27319 } 27320 27321 next_device: 27322 free(NAME); 27323 free(DESC); 27324 free(IOID); 27325 ppNextDeviceHint += 1; 27326 27327 /* We need to stop enumeration if the callback returned false. */ 27328 if (stopEnumeration) { 27329 break; 27330 } 27331 } 27332 27333 ma_free(pUniqueIDs, &pContext->allocationCallbacks); 27334 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); 27335 27336 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); 27337 27338 return MA_SUCCESS; 27339 } 27340 27341 27342 typedef struct 27343 { 27344 ma_device_type deviceType; 27345 const ma_device_id* pDeviceID; 27346 ma_share_mode shareMode; 27347 ma_device_info* pDeviceInfo; 27348 ma_bool32 foundDevice; 27349 } ma_context_get_device_info_enum_callback_data__alsa; 27350 27351 static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) 27352 { 27353 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; 27354 MA_ASSERT(pData != NULL); 27355 27356 (void)pContext; 27357 27358 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { 27359 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); 27360 pData->foundDevice = MA_TRUE; 27361 } else { 27362 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { 27363 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); 27364 pData->foundDevice = MA_TRUE; 27365 } 27366 } 27367 27368 /* Keep enumerating until we have found the device. */ 27369 return !pData->foundDevice; 27370 } 27371 27372 static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) 27373 { 27374 MA_ASSERT(pPCM != NULL); 27375 MA_ASSERT(pHWParams != NULL); 27376 MA_ASSERT(pDeviceInfo != NULL); 27377 27378 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { 27379 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; 27380 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 27381 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; 27382 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; 27383 pDeviceInfo->nativeDataFormatCount += 1; 27384 } 27385 } 27386 27387 static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) 27388 { 27389 ma_uint32 iSampleRate; 27390 unsigned int minSampleRate; 27391 unsigned int maxSampleRate; 27392 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ 27393 27394 /* There could be a range. */ 27395 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); 27396 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); 27397 27398 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ 27399 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); 27400 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); 27401 27402 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { 27403 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; 27404 27405 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { 27406 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); 27407 } 27408 } 27409 27410 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ 27411 if (!ma_is_standard_sample_rate(minSampleRate)) { 27412 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); 27413 } 27414 27415 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { 27416 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); 27417 } 27418 } 27419 27420 static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 27421 { 27422 ma_context_get_device_info_enum_callback_data__alsa data; 27423 ma_result result; 27424 int resultALSA; 27425 ma_snd_pcm_t* pPCM; 27426 ma_snd_pcm_hw_params_t* pHWParams; 27427 ma_uint32 iFormat; 27428 ma_uint32 iChannel; 27429 27430 MA_ASSERT(pContext != NULL); 27431 27432 /* We just enumerate to find basic information about the device. */ 27433 data.deviceType = deviceType; 27434 data.pDeviceID = pDeviceID; 27435 data.pDeviceInfo = pDeviceInfo; 27436 data.foundDevice = MA_FALSE; 27437 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); 27438 if (result != MA_SUCCESS) { 27439 return result; 27440 } 27441 27442 if (!data.foundDevice) { 27443 return MA_NO_DEVICE; 27444 } 27445 27446 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { 27447 pDeviceInfo->isDefault = MA_TRUE; 27448 } 27449 27450 /* For detailed info we need to open the device. */ 27451 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); 27452 if (result != MA_SUCCESS) { 27453 return result; 27454 } 27455 27456 /* We need to initialize a HW parameters object in order to know what formats are supported. */ 27457 pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); 27458 if (pHWParams == NULL) { 27459 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); 27460 return MA_OUT_OF_MEMORY; 27461 } 27462 27463 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); 27464 if (resultALSA < 0) { 27465 ma_free(pHWParams, &pContext->allocationCallbacks); 27466 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); 27467 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); 27468 return ma_result_from_errno(-resultALSA); 27469 } 27470 27471 /* 27472 Some ALSA devices can support many permutations of formats, channels and rates. We only support 27473 a fixed number of permutations which means we need to employ some strategies to ensure the best 27474 combinations are returned. An example is the "pulse" device which can do it's own data conversion 27475 in software and as a result can support any combination of format, channels and rate. 27476 27477 We want to ensure the the first data formats are the best. We have a list of favored sample 27478 formats and sample rates, so these will be the basis of our iteration. 27479 */ 27480 27481 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ 27482 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { 27483 ma_format format = g_maFormatPriorities[iFormat]; 27484 27485 /* 27486 For each format we need to make sure we reset the configuration space so we don't return 27487 channel counts and rates that aren't compatible with a format. 27488 */ 27489 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); 27490 27491 /* Test the format first. If this fails it means the format is not supported and we can skip it. */ 27492 if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { 27493 /* The format is supported. */ 27494 unsigned int minChannels; 27495 unsigned int maxChannels; 27496 27497 /* 27498 The configuration space needs to be restricted to this format so we can get an accurate 27499 picture of which sample rates and channel counts are support with this format. 27500 */ 27501 ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); 27502 27503 /* Now we need to check for supported channels. */ 27504 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); 27505 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); 27506 27507 if (minChannels > MA_MAX_CHANNELS) { 27508 continue; /* Too many channels. */ 27509 } 27510 if (maxChannels < MA_MIN_CHANNELS) { 27511 continue; /* Not enough channels. */ 27512 } 27513 27514 /* 27515 Make sure the channel count is clamped. This is mainly intended for the max channels 27516 because some devices can report an unbound maximum. 27517 */ 27518 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); 27519 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); 27520 27521 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { 27522 /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ 27523 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ 27524 } else { 27525 /* The device only supports a specific set of channels. We need to iterate over all of them. */ 27526 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { 27527 /* Test the channel before applying it to the configuration space. */ 27528 unsigned int channels = iChannel; 27529 27530 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ 27531 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); 27532 ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); 27533 27534 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { 27535 /* The channel count is supported. */ 27536 27537 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ 27538 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); 27539 27540 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ 27541 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); 27542 } else { 27543 /* The channel count is not supported. Skip. */ 27544 } 27545 } 27546 } 27547 } else { 27548 /* The format is not supported. Skip. */ 27549 } 27550 } 27551 27552 ma_free(pHWParams, &pContext->allocationCallbacks); 27553 27554 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); 27555 return MA_SUCCESS; 27556 } 27557 27558 static ma_result ma_device_uninit__alsa(ma_device* pDevice) 27559 { 27560 MA_ASSERT(pDevice != NULL); 27561 27562 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { 27563 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); 27564 close(pDevice->alsa.wakeupfdCapture); 27565 ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); 27566 } 27567 27568 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { 27569 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); 27570 close(pDevice->alsa.wakeupfdPlayback); 27571 ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); 27572 } 27573 27574 return MA_SUCCESS; 27575 } 27576 27577 static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) 27578 { 27579 ma_result result; 27580 int resultALSA; 27581 ma_snd_pcm_t* pPCM; 27582 ma_bool32 isUsingMMap; 27583 ma_snd_pcm_format_t formatALSA; 27584 ma_format internalFormat; 27585 ma_uint32 internalChannels; 27586 ma_uint32 internalSampleRate; 27587 ma_channel internalChannelMap[MA_MAX_CHANNELS]; 27588 ma_uint32 internalPeriodSizeInFrames; 27589 ma_uint32 internalPeriods; 27590 int openMode; 27591 ma_snd_pcm_hw_params_t* pHWParams; 27592 ma_snd_pcm_sw_params_t* pSWParams; 27593 ma_snd_pcm_uframes_t bufferBoundary; 27594 int pollDescriptorCount; 27595 struct pollfd* pPollDescriptors; 27596 int wakeupfd; 27597 27598 MA_ASSERT(pConfig != NULL); 27599 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ 27600 MA_ASSERT(pDevice != NULL); 27601 27602 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); 27603 27604 openMode = 0; 27605 if (pConfig->alsa.noAutoResample) { 27606 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; 27607 } 27608 if (pConfig->alsa.noAutoChannels) { 27609 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; 27610 } 27611 if (pConfig->alsa.noAutoFormat) { 27612 openMode |= MA_SND_PCM_NO_AUTO_FORMAT; 27613 } 27614 27615 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); 27616 if (result != MA_SUCCESS) { 27617 return result; 27618 } 27619 27620 27621 /* Hardware parameters. */ 27622 pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); 27623 if (pHWParams == NULL) { 27624 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27625 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); 27626 return MA_OUT_OF_MEMORY; 27627 } 27628 27629 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); 27630 if (resultALSA < 0) { 27631 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27632 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27633 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); 27634 return ma_result_from_errno(-resultALSA); 27635 } 27636 27637 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ 27638 isUsingMMap = MA_FALSE; 27639 #if 0 /* NOTE: MMAP mode temporarily disabled. */ 27640 if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ 27641 if (!pConfig->alsa.noMMap) { 27642 if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { 27643 pDevice->alsa.isUsingMMap = MA_TRUE; 27644 } 27645 } 27646 } 27647 #endif 27648 27649 if (!isUsingMMap) { 27650 resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); 27651 if (resultALSA < 0) { 27652 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27653 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27654 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); 27655 return ma_result_from_errno(-resultALSA); 27656 } 27657 } 27658 27659 /* 27660 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't 27661 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. 27662 */ 27663 27664 /* Format. */ 27665 { 27666 /* 27667 At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is 27668 supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. 27669 */ 27670 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { 27671 /* We're either requesting the native format or the specified format is not supported. */ 27672 size_t iFormat; 27673 27674 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; 27675 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { 27676 if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { 27677 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); 27678 break; 27679 } 27680 } 27681 27682 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { 27683 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27684 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27685 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); 27686 return MA_FORMAT_NOT_SUPPORTED; 27687 } 27688 } 27689 27690 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); 27691 if (resultALSA < 0) { 27692 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27693 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27694 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); 27695 return ma_result_from_errno(-resultALSA); 27696 } 27697 27698 internalFormat = ma_format_from_alsa(formatALSA); 27699 if (internalFormat == ma_format_unknown) { 27700 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27701 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27702 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); 27703 return MA_FORMAT_NOT_SUPPORTED; 27704 } 27705 } 27706 27707 /* Channels. */ 27708 { 27709 unsigned int channels = pDescriptor->channels; 27710 if (channels == 0) { 27711 channels = MA_DEFAULT_CHANNELS; 27712 } 27713 27714 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); 27715 if (resultALSA < 0) { 27716 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27717 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27718 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); 27719 return ma_result_from_errno(-resultALSA); 27720 } 27721 27722 internalChannels = (ma_uint32)channels; 27723 } 27724 27725 /* Sample Rate */ 27726 { 27727 unsigned int sampleRate; 27728 27729 /* 27730 It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes 27731 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable 27732 resampling. 27733 27734 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a 27735 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling 27736 doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly 27737 faster rate. 27738 27739 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine 27740 for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very 27741 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. 27742 27743 I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce 27744 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. 27745 */ 27746 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); 27747 27748 sampleRate = pDescriptor->sampleRate; 27749 if (sampleRate == 0) { 27750 sampleRate = MA_DEFAULT_SAMPLE_RATE; 27751 } 27752 27753 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); 27754 if (resultALSA < 0) { 27755 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27756 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27757 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); 27758 return ma_result_from_errno(-resultALSA); 27759 } 27760 27761 internalSampleRate = (ma_uint32)sampleRate; 27762 } 27763 27764 /* Periods. */ 27765 { 27766 ma_uint32 periods = pDescriptor->periodCount; 27767 27768 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); 27769 if (resultALSA < 0) { 27770 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27771 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27772 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); 27773 return ma_result_from_errno(-resultALSA); 27774 } 27775 27776 internalPeriods = periods; 27777 } 27778 27779 /* Buffer Size */ 27780 { 27781 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; 27782 27783 resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); 27784 if (resultALSA < 0) { 27785 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27786 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27787 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); 27788 return ma_result_from_errno(-resultALSA); 27789 } 27790 27791 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; 27792 } 27793 27794 /* Apply hardware parameters. */ 27795 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); 27796 if (resultALSA < 0) { 27797 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27798 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27799 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); 27800 return ma_result_from_errno(-resultALSA); 27801 } 27802 27803 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); 27804 pHWParams = NULL; 27805 27806 27807 /* Software parameters. */ 27808 pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); 27809 if (pSWParams == NULL) { 27810 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27811 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); 27812 return MA_OUT_OF_MEMORY; 27813 } 27814 27815 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); 27816 if (resultALSA < 0) { 27817 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27818 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27819 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); 27820 return ma_result_from_errno(-resultALSA); 27821 } 27822 27823 resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); 27824 if (resultALSA < 0) { 27825 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27826 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27827 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); 27828 return ma_result_from_errno(-resultALSA); 27829 } 27830 27831 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); 27832 if (resultALSA < 0) { 27833 bufferBoundary = internalPeriodSizeInFrames * internalPeriods; 27834 } 27835 27836 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ 27837 /* 27838 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to 27839 the size of a period. But for full-duplex we need to set it such that it is at least two periods. 27840 */ 27841 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); 27842 if (resultALSA < 0) { 27843 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27844 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27845 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); 27846 return ma_result_from_errno(-resultALSA); 27847 } 27848 27849 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); 27850 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ 27851 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27852 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27853 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); 27854 return ma_result_from_errno(-resultALSA); 27855 } 27856 } 27857 27858 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); 27859 if (resultALSA < 0) { 27860 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27861 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27862 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); 27863 return ma_result_from_errno(-resultALSA); 27864 } 27865 27866 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); 27867 pSWParams = NULL; 27868 27869 27870 /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ 27871 { 27872 ma_snd_pcm_chmap_t* pChmap = NULL; 27873 if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { 27874 pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); 27875 } 27876 27877 if (pChmap != NULL) { 27878 ma_uint32 iChannel; 27879 27880 /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ 27881 if (pChmap->channels >= internalChannels) { 27882 /* Drop excess channels. */ 27883 for (iChannel = 0; iChannel < internalChannels; ++iChannel) { 27884 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); 27885 } 27886 } else { 27887 ma_uint32 i; 27888 27889 /* 27890 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate 27891 channels. If validation fails, fall back to defaults. 27892 */ 27893 ma_bool32 isValid = MA_TRUE; 27894 27895 /* Fill with defaults. */ 27896 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); 27897 27898 /* Overwrite first pChmap->channels channels. */ 27899 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { 27900 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); 27901 } 27902 27903 /* Validate. */ 27904 for (i = 0; i < internalChannels && isValid; ++i) { 27905 ma_uint32 j; 27906 for (j = i+1; j < internalChannels; ++j) { 27907 if (internalChannelMap[i] == internalChannelMap[j]) { 27908 isValid = MA_FALSE; 27909 break; 27910 } 27911 } 27912 } 27913 27914 /* If our channel map is invalid, fall back to defaults. */ 27915 if (!isValid) { 27916 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); 27917 } 27918 } 27919 27920 free(pChmap); 27921 pChmap = NULL; 27922 } else { 27923 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ 27924 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); 27925 } 27926 } 27927 27928 27929 /* 27930 We need to retrieve the poll descriptors so we can use poll() to wait for data to become 27931 available for reading or writing. There's no well defined maximum for this so we're just going 27932 to allocate this on the heap. 27933 */ 27934 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); 27935 if (pollDescriptorCount <= 0) { 27936 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27937 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); 27938 return MA_ERROR; 27939 } 27940 27941 pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ 27942 if (pPollDescriptors == NULL) { 27943 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27944 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); 27945 return MA_OUT_OF_MEMORY; 27946 } 27947 27948 /* 27949 We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver 27950 never returns from writei() and readi(). This has been observed with the "pulse" device. 27951 */ 27952 wakeupfd = eventfd(0, 0); 27953 if (wakeupfd < 0) { 27954 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); 27955 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27956 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); 27957 return ma_result_from_errno(errno); 27958 } 27959 27960 /* We'll place the wakeup fd at the start of the buffer. */ 27961 pPollDescriptors[0].fd = wakeupfd; 27962 pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ 27963 pPollDescriptors[0].revents = 0; 27964 27965 /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ 27966 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ 27967 if (pollDescriptorCount <= 0) { 27968 close(wakeupfd); 27969 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); 27970 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27971 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); 27972 return MA_ERROR; 27973 } 27974 27975 if (deviceType == ma_device_type_capture) { 27976 pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; 27977 pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; 27978 pDevice->alsa.wakeupfdCapture = wakeupfd; 27979 } else { 27980 pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; 27981 pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; 27982 pDevice->alsa.wakeupfdPlayback = wakeupfd; 27983 } 27984 27985 27986 /* We're done. Prepare the device. */ 27987 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); 27988 if (resultALSA < 0) { 27989 close(wakeupfd); 27990 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); 27991 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); 27992 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); 27993 return ma_result_from_errno(-resultALSA); 27994 } 27995 27996 27997 if (deviceType == ma_device_type_capture) { 27998 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; 27999 pDevice->alsa.isUsingMMapCapture = isUsingMMap; 28000 } else { 28001 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; 28002 pDevice->alsa.isUsingMMapPlayback = isUsingMMap; 28003 } 28004 28005 pDescriptor->format = internalFormat; 28006 pDescriptor->channels = internalChannels; 28007 pDescriptor->sampleRate = internalSampleRate; 28008 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); 28009 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; 28010 pDescriptor->periodCount = internalPeriods; 28011 28012 return MA_SUCCESS; 28013 } 28014 28015 static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 28016 { 28017 MA_ASSERT(pDevice != NULL); 28018 28019 MA_ZERO_OBJECT(&pDevice->alsa); 28020 28021 if (pConfig->deviceType == ma_device_type_loopback) { 28022 return MA_DEVICE_TYPE_NOT_SUPPORTED; 28023 } 28024 28025 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 28026 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); 28027 if (result != MA_SUCCESS) { 28028 return result; 28029 } 28030 } 28031 28032 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 28033 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); 28034 if (result != MA_SUCCESS) { 28035 return result; 28036 } 28037 } 28038 28039 return MA_SUCCESS; 28040 } 28041 28042 static ma_result ma_device_start__alsa(ma_device* pDevice) 28043 { 28044 int resultALSA; 28045 28046 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 28047 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); 28048 if (resultALSA < 0) { 28049 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); 28050 return ma_result_from_errno(-resultALSA); 28051 } 28052 } 28053 28054 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 28055 /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ 28056 } 28057 28058 return MA_SUCCESS; 28059 } 28060 28061 static ma_result ma_device_stop__alsa(ma_device* pDevice) 28062 { 28063 /* 28064 The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is 28065 a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. 28066 */ 28067 int resultPoll; 28068 28069 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 28070 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); 28071 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); 28072 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); 28073 28074 /* We need to prepare the device again, otherwise we won't be able to restart the device. */ 28075 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); 28076 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { 28077 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); 28078 } else { 28079 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); 28080 } 28081 28082 /* Clear the wakeupfd. */ 28083 resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); 28084 if (resultPoll > 0) { 28085 ma_uint64 t; 28086 read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); 28087 } 28088 } 28089 28090 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 28091 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); 28092 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); 28093 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); 28094 28095 /* We need to prepare the device again, otherwise we won't be able to restart the device. */ 28096 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); 28097 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { 28098 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); 28099 } else { 28100 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); 28101 } 28102 28103 /* Clear the wakeupfd. */ 28104 resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); 28105 if (resultPoll > 0) { 28106 ma_uint64 t; 28107 read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); 28108 } 28109 28110 } 28111 28112 return MA_SUCCESS; 28113 } 28114 28115 static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) 28116 { 28117 for (;;) { 28118 unsigned short revents; 28119 int resultALSA; 28120 int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); 28121 if (resultPoll < 0) { 28122 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); 28123 return ma_result_from_errno(errno); 28124 } 28125 28126 /* 28127 Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor 28128 has had it's POLLIN flag set. If so, we need to actually read the data and then exit 28129 function. The wakeup descriptor will be the first item in the descriptors buffer. 28130 */ 28131 if ((pPollDescriptors[0].revents & POLLIN) != 0) { 28132 ma_uint64 t; 28133 int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ 28134 if (resultRead < 0) { 28135 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); 28136 return ma_result_from_errno(errno); 28137 } 28138 28139 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); 28140 return MA_DEVICE_NOT_STARTED; 28141 } 28142 28143 /* 28144 Getting here means that some data should be able to be read. We need to use ALSA to 28145 translate the revents flags for us. 28146 */ 28147 resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ 28148 if (resultALSA < 0) { 28149 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); 28150 return ma_result_from_errno(-resultALSA); 28151 } 28152 28153 if ((revents & POLLERR) != 0) { 28154 ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); 28155 if (state == MA_SND_PCM_STATE_XRUN) { 28156 /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ 28157 } else { 28158 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); 28159 } 28160 } 28161 28162 if ((revents & requiredEvent) == requiredEvent) { 28163 break; /* We're done. Data available for reading or writing. */ 28164 } 28165 } 28166 28167 return MA_SUCCESS; 28168 } 28169 28170 static ma_result ma_device_wait_read__alsa(ma_device* pDevice) 28171 { 28172 return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ 28173 } 28174 28175 static ma_result ma_device_wait_write__alsa(ma_device* pDevice) 28176 { 28177 return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ 28178 } 28179 28180 static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) 28181 { 28182 ma_snd_pcm_sframes_t resultALSA = 0; 28183 28184 MA_ASSERT(pDevice != NULL); 28185 MA_ASSERT(pFramesOut != NULL); 28186 28187 if (pFramesRead != NULL) { 28188 *pFramesRead = 0; 28189 } 28190 28191 while (ma_device_get_state(pDevice) == ma_device_state_started) { 28192 ma_result result; 28193 28194 /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ 28195 result = ma_device_wait_read__alsa(pDevice); 28196 if (result != MA_SUCCESS) { 28197 return result; 28198 } 28199 28200 /* Getting here means we should have data available. */ 28201 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); 28202 if (resultALSA >= 0) { 28203 break; /* Success. */ 28204 } else { 28205 if (resultALSA == -EAGAIN) { 28206 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ 28207 continue; /* Try again. */ 28208 } else if (resultALSA == -EPIPE) { 28209 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); 28210 28211 /* Overrun. Recover and try again. If this fails we need to return an error. */ 28212 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); 28213 if (resultALSA < 0) { 28214 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); 28215 return ma_result_from_errno((int)-resultALSA); 28216 } 28217 28218 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); 28219 if (resultALSA < 0) { 28220 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); 28221 return ma_result_from_errno((int)-resultALSA); 28222 } 28223 28224 continue; /* Try reading again. */ 28225 } 28226 } 28227 } 28228 28229 if (pFramesRead != NULL) { 28230 *pFramesRead = resultALSA; 28231 } 28232 28233 return MA_SUCCESS; 28234 } 28235 28236 static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 28237 { 28238 ma_snd_pcm_sframes_t resultALSA = 0; 28239 28240 MA_ASSERT(pDevice != NULL); 28241 MA_ASSERT(pFrames != NULL); 28242 28243 if (pFramesWritten != NULL) { 28244 *pFramesWritten = 0; 28245 } 28246 28247 while (ma_device_get_state(pDevice) == ma_device_state_started) { 28248 ma_result result; 28249 28250 /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ 28251 result = ma_device_wait_write__alsa(pDevice); 28252 if (result != MA_SUCCESS) { 28253 return result; 28254 } 28255 28256 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); 28257 if (resultALSA >= 0) { 28258 break; /* Success. */ 28259 } else { 28260 if (resultALSA == -EAGAIN) { 28261 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ 28262 continue; /* Try again. */ 28263 } else if (resultALSA == -EPIPE) { 28264 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); 28265 28266 /* Underrun. Recover and try again. If this fails we need to return an error. */ 28267 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ 28268 if (resultALSA < 0) { 28269 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); 28270 return ma_result_from_errno((int)-resultALSA); 28271 } 28272 28273 /* 28274 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it 28275 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of 28276 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure 28277 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't 28278 quite right here. 28279 */ 28280 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); 28281 if (resultALSA < 0) { 28282 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); 28283 return ma_result_from_errno((int)-resultALSA); 28284 } 28285 28286 continue; /* Try writing again. */ 28287 } 28288 } 28289 } 28290 28291 if (pFramesWritten != NULL) { 28292 *pFramesWritten = resultALSA; 28293 } 28294 28295 return MA_SUCCESS; 28296 } 28297 28298 static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) 28299 { 28300 ma_uint64 t = 1; 28301 int resultWrite = 0; 28302 28303 MA_ASSERT(pDevice != NULL); 28304 28305 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); 28306 28307 /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ 28308 if (pDevice->alsa.pPollDescriptorsCapture != NULL) { 28309 resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); 28310 } 28311 if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { 28312 resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); 28313 } 28314 28315 if (resultWrite < 0) { 28316 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); 28317 return ma_result_from_errno(errno); 28318 } 28319 28320 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); 28321 28322 return MA_SUCCESS; 28323 } 28324 28325 static ma_result ma_context_uninit__alsa(ma_context* pContext) 28326 { 28327 MA_ASSERT(pContext != NULL); 28328 MA_ASSERT(pContext->backend == ma_backend_alsa); 28329 28330 /* Clean up memory for memory leak checkers. */ 28331 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); 28332 28333 #ifndef MA_NO_RUNTIME_LINKING 28334 ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); 28335 #endif 28336 28337 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); 28338 28339 return MA_SUCCESS; 28340 } 28341 28342 static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 28343 { 28344 ma_result result; 28345 #ifndef MA_NO_RUNTIME_LINKING 28346 const char* libasoundNames[] = { 28347 "libasound.so.2", 28348 "libasound.so" 28349 }; 28350 size_t i; 28351 28352 for (i = 0; i < ma_countof(libasoundNames); ++i) { 28353 pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); 28354 if (pContext->alsa.asoundSO != NULL) { 28355 break; 28356 } 28357 } 28358 28359 if (pContext->alsa.asoundSO == NULL) { 28360 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); 28361 return MA_NO_BACKEND; 28362 } 28363 28364 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); 28365 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); 28366 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); 28367 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); 28368 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); 28369 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); 28370 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); 28371 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); 28372 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); 28373 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); 28374 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); 28375 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); 28376 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); 28377 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); 28378 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); 28379 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); 28380 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); 28381 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); 28382 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); 28383 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); 28384 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); 28385 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); 28386 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); 28387 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); 28388 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); 28389 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); 28390 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); 28391 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); 28392 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); 28393 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); 28394 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); 28395 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); 28396 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); 28397 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); 28398 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); 28399 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); 28400 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); 28401 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); 28402 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); 28403 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); 28404 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); 28405 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); 28406 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); 28407 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); 28408 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); 28409 pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); 28410 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); 28411 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); 28412 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); 28413 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); 28414 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); 28415 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); 28416 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); 28417 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); 28418 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); 28419 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); 28420 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); 28421 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); 28422 pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); 28423 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); 28424 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); 28425 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); 28426 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); 28427 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); 28428 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); 28429 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); 28430 #else 28431 /* The system below is just for type safety. */ 28432 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; 28433 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; 28434 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; 28435 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; 28436 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; 28437 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; 28438 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; 28439 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; 28440 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; 28441 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; 28442 ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; 28443 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; 28444 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; 28445 ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; 28446 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; 28447 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; 28448 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; 28449 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; 28450 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; 28451 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; 28452 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; 28453 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; 28454 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; 28455 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; 28456 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; 28457 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; 28458 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; 28459 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; 28460 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; 28461 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; 28462 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; 28463 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; 28464 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; 28465 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; 28466 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; 28467 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; 28468 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; 28469 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; 28470 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; 28471 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; 28472 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; 28473 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; 28474 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; 28475 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; 28476 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; 28477 ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; 28478 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; 28479 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; 28480 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; 28481 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; 28482 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; 28483 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; 28484 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; 28485 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; 28486 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; 28487 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; 28488 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; 28489 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; 28490 ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; 28491 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; 28492 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; 28493 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; 28494 ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; 28495 ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; 28496 ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; 28497 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; 28498 28499 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; 28500 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; 28501 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; 28502 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; 28503 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; 28504 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; 28505 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; 28506 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; 28507 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; 28508 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; 28509 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; 28510 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; 28511 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; 28512 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; 28513 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; 28514 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; 28515 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; 28516 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; 28517 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; 28518 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; 28519 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; 28520 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; 28521 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; 28522 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; 28523 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; 28524 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; 28525 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; 28526 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; 28527 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; 28528 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; 28529 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; 28530 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; 28531 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; 28532 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; 28533 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; 28534 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; 28535 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; 28536 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; 28537 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; 28538 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; 28539 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; 28540 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; 28541 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; 28542 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; 28543 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; 28544 pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; 28545 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; 28546 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; 28547 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; 28548 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; 28549 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; 28550 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; 28551 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; 28552 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; 28553 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; 28554 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; 28555 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; 28556 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; 28557 pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; 28558 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; 28559 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; 28560 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; 28561 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; 28562 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; 28563 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; 28564 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; 28565 #endif 28566 28567 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; 28568 28569 result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); 28570 if (result != MA_SUCCESS) { 28571 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); 28572 return result; 28573 } 28574 28575 pCallbacks->onContextInit = ma_context_init__alsa; 28576 pCallbacks->onContextUninit = ma_context_uninit__alsa; 28577 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; 28578 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; 28579 pCallbacks->onDeviceInit = ma_device_init__alsa; 28580 pCallbacks->onDeviceUninit = ma_device_uninit__alsa; 28581 pCallbacks->onDeviceStart = ma_device_start__alsa; 28582 pCallbacks->onDeviceStop = ma_device_stop__alsa; 28583 pCallbacks->onDeviceRead = ma_device_read__alsa; 28584 pCallbacks->onDeviceWrite = ma_device_write__alsa; 28585 pCallbacks->onDeviceDataLoop = NULL; 28586 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; 28587 28588 return MA_SUCCESS; 28589 } 28590 #endif /* ALSA */ 28591 28592 28593 28594 /****************************************************************************** 28595 28596 PulseAudio Backend 28597 28598 ******************************************************************************/ 28599 #ifdef MA_HAS_PULSEAUDIO 28600 /* 28601 The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on 28602 in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. 28603 28604 PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it 28605 allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it 28606 appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or 28607 write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the 28608 simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient 28609 when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. 28610 28611 Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to 28612 get fun, and I don't mean that in a good way... 28613 28614 The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands 28615 don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is 28616 enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost 28617 all of PulseAudio's problems stem from. 28618 28619 When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own 28620 vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called 28621 pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop 28622 because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use 28623 it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. 28624 28625 To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer 28626 to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded 28627 main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely 28628 specialized such as if you want to integrate it into your application's existing main loop infrastructure. 28629 28630 (EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. 28631 It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) 28632 28633 Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to 28634 miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's 28635 one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which 28636 is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if 28637 you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` 28638 has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can 28639 set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. 28640 All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. 28641 This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before 28642 attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. 28643 28644 The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an 28645 internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the 28646 host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. 28647 28648 Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. 28649 The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call 28650 `pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get 28651 information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object 28652 is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to 28653 run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the 28654 context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. 28655 All of that just to retrieve basic information about a device! 28656 28657 Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the 28658 context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design 28659 choices in PulseAudio. 28660 28661 PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here 28662 because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for 28663 writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can 28664 set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices 28665 straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, 28666 PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) 28667 because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback 28668 would be where a program will want to write or read data to or from the stream, but when it's called before the application has even 28669 requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at 28670 that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the 28671 stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data 28672 callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio 28673 doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been 28674 started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data 28675 callback is not fired. 28676 28677 This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will 28678 continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device 28679 is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in 28680 PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call 28681 `pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always 28682 writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if 28683 you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to 28684 *not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining 28685 important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained 28686 before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write 28687 data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! 28688 28689 This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* 28690 write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just 28691 resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This 28692 disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the 28693 callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) 28694 28695 Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, 28696 only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as 28697 "corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think 28698 it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you 28699 guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is 28700 absolutely beyond me. Would it really be that hard to just make it run synchronously? 28701 28702 Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that 28703 they were initialized in. 28704 28705 That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're 28706 embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to 28707 run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche 28708 requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is 28709 constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a 28710 parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These 28711 changes alone will change PulseAudio from one of the worst audio APIs to one of the best. 28712 */ 28713 28714 28715 /* 28716 It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header 28717 to check for type safety. We cannot do this when linking at run time because the header might not be available. 28718 */ 28719 #ifdef MA_NO_RUNTIME_LINKING 28720 28721 /* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ 28722 #if !defined(__cplusplus) 28723 #if defined(__STRICT_ANSI__) 28724 #if !defined(inline) 28725 #define inline __inline__ __attribute__((always_inline)) 28726 #define MA_INLINE_DEFINED 28727 #endif 28728 #endif 28729 #endif 28730 #include <pulse/pulseaudio.h> 28731 #if defined(MA_INLINE_DEFINED) 28732 #undef inline 28733 #undef MA_INLINE_DEFINED 28734 #endif 28735 28736 #define MA_PA_OK PA_OK 28737 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS 28738 #define MA_PA_ERR_INVALID PA_ERR_INVALID 28739 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY 28740 #define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED 28741 28742 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX 28743 #define MA_PA_RATE_MAX PA_RATE_MAX 28744 28745 typedef pa_context_flags_t ma_pa_context_flags_t; 28746 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS 28747 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN 28748 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL 28749 28750 typedef pa_stream_flags_t ma_pa_stream_flags_t; 28751 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS 28752 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED 28753 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING 28754 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC 28755 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE 28756 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS 28757 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS 28758 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT 28759 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE 28760 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS 28761 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE 28762 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE 28763 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT 28764 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED 28765 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY 28766 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS 28767 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 28768 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED 28769 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND 28770 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME 28771 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH 28772 28773 typedef pa_sink_flags_t ma_pa_sink_flags_t; 28774 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS 28775 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL 28776 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY 28777 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE 28778 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK 28779 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL 28780 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME 28781 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME 28782 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY 28783 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS 28784 28785 typedef pa_source_flags_t ma_pa_source_flags_t; 28786 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS 28787 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL 28788 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY 28789 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE 28790 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK 28791 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL 28792 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME 28793 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY 28794 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME 28795 28796 typedef pa_context_state_t ma_pa_context_state_t; 28797 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED 28798 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING 28799 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING 28800 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME 28801 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY 28802 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED 28803 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED 28804 28805 typedef pa_stream_state_t ma_pa_stream_state_t; 28806 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED 28807 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING 28808 #define MA_PA_STREAM_READY PA_STREAM_READY 28809 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED 28810 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED 28811 28812 typedef pa_operation_state_t ma_pa_operation_state_t; 28813 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING 28814 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE 28815 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED 28816 28817 typedef pa_sink_state_t ma_pa_sink_state_t; 28818 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE 28819 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING 28820 #define MA_PA_SINK_IDLE PA_SINK_IDLE 28821 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED 28822 28823 typedef pa_source_state_t ma_pa_source_state_t; 28824 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE 28825 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING 28826 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE 28827 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED 28828 28829 typedef pa_seek_mode_t ma_pa_seek_mode_t; 28830 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE 28831 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE 28832 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ 28833 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END 28834 28835 typedef pa_channel_position_t ma_pa_channel_position_t; 28836 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID 28837 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO 28838 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT 28839 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT 28840 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER 28841 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER 28842 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT 28843 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT 28844 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE 28845 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 28846 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 28847 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT 28848 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT 28849 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 28850 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 28851 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 28852 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 28853 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 28854 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 28855 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 28856 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 28857 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 28858 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 28859 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 28860 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 28861 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 28862 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 28863 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 28864 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 28865 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 28866 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 28867 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 28868 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 28869 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 28870 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 28871 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 28872 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 28873 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 28874 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 28875 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 28876 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 28877 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 28878 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 28879 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 28880 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 28881 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER 28882 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT 28883 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 28884 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER 28885 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT 28886 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT 28887 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER 28888 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT 28889 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT 28890 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER 28891 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER 28892 28893 typedef pa_channel_map_def_t ma_pa_channel_map_def_t; 28894 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF 28895 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA 28896 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX 28897 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX 28898 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS 28899 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT 28900 28901 typedef pa_sample_format_t ma_pa_sample_format_t; 28902 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID 28903 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 28904 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW 28905 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW 28906 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE 28907 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE 28908 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE 28909 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE 28910 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE 28911 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE 28912 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE 28913 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE 28914 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE 28915 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE 28916 28917 typedef pa_mainloop ma_pa_mainloop; 28918 typedef pa_threaded_mainloop ma_pa_threaded_mainloop; 28919 typedef pa_mainloop_api ma_pa_mainloop_api; 28920 typedef pa_context ma_pa_context; 28921 typedef pa_operation ma_pa_operation; 28922 typedef pa_stream ma_pa_stream; 28923 typedef pa_spawn_api ma_pa_spawn_api; 28924 typedef pa_buffer_attr ma_pa_buffer_attr; 28925 typedef pa_channel_map ma_pa_channel_map; 28926 typedef pa_cvolume ma_pa_cvolume; 28927 typedef pa_sample_spec ma_pa_sample_spec; 28928 typedef pa_sink_info ma_pa_sink_info; 28929 typedef pa_source_info ma_pa_source_info; 28930 28931 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; 28932 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; 28933 typedef pa_source_info_cb_t ma_pa_source_info_cb_t; 28934 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; 28935 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; 28936 typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; 28937 typedef pa_free_cb_t ma_pa_free_cb_t; 28938 #else 28939 #define MA_PA_OK 0 28940 #define MA_PA_ERR_ACCESS 1 28941 #define MA_PA_ERR_INVALID 2 28942 #define MA_PA_ERR_NOENTITY 5 28943 #define MA_PA_ERR_NOTSUPPORTED 19 28944 28945 #define MA_PA_CHANNELS_MAX 32 28946 #define MA_PA_RATE_MAX 384000 28947 28948 typedef int ma_pa_context_flags_t; 28949 #define MA_PA_CONTEXT_NOFLAGS 0x00000000 28950 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 28951 #define MA_PA_CONTEXT_NOFAIL 0x00000002 28952 28953 typedef int ma_pa_stream_flags_t; 28954 #define MA_PA_STREAM_NOFLAGS 0x00000000 28955 #define MA_PA_STREAM_START_CORKED 0x00000001 28956 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 28957 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 28958 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 28959 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 28960 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 28961 #define MA_PA_STREAM_FIX_FORMAT 0x00000040 28962 #define MA_PA_STREAM_FIX_RATE 0x00000080 28963 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100 28964 #define MA_PA_STREAM_DONT_MOVE 0x00000200 28965 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400 28966 #define MA_PA_STREAM_PEAK_DETECT 0x00000800 28967 #define MA_PA_STREAM_START_MUTED 0x00001000 28968 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 28969 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 28970 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 28971 #define MA_PA_STREAM_START_UNMUTED 0x00010000 28972 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 28973 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 28974 #define MA_PA_STREAM_PASSTHROUGH 0x00080000 28975 28976 typedef int ma_pa_sink_flags_t; 28977 #define MA_PA_SINK_NOFLAGS 0x00000000 28978 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 28979 #define MA_PA_SINK_LATENCY 0x00000002 28980 #define MA_PA_SINK_HARDWARE 0x00000004 28981 #define MA_PA_SINK_NETWORK 0x00000008 28982 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 28983 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 28984 #define MA_PA_SINK_FLAT_VOLUME 0x00000040 28985 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 28986 #define MA_PA_SINK_SET_FORMATS 0x00000100 28987 28988 typedef int ma_pa_source_flags_t; 28989 #define MA_PA_SOURCE_NOFLAGS 0x00000000 28990 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 28991 #define MA_PA_SOURCE_LATENCY 0x00000002 28992 #define MA_PA_SOURCE_HARDWARE 0x00000004 28993 #define MA_PA_SOURCE_NETWORK 0x00000008 28994 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 28995 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 28996 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 28997 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 28998 28999 typedef int ma_pa_context_state_t; 29000 #define MA_PA_CONTEXT_UNCONNECTED 0 29001 #define MA_PA_CONTEXT_CONNECTING 1 29002 #define MA_PA_CONTEXT_AUTHORIZING 2 29003 #define MA_PA_CONTEXT_SETTING_NAME 3 29004 #define MA_PA_CONTEXT_READY 4 29005 #define MA_PA_CONTEXT_FAILED 5 29006 #define MA_PA_CONTEXT_TERMINATED 6 29007 29008 typedef int ma_pa_stream_state_t; 29009 #define MA_PA_STREAM_UNCONNECTED 0 29010 #define MA_PA_STREAM_CREATING 1 29011 #define MA_PA_STREAM_READY 2 29012 #define MA_PA_STREAM_FAILED 3 29013 #define MA_PA_STREAM_TERMINATED 4 29014 29015 typedef int ma_pa_operation_state_t; 29016 #define MA_PA_OPERATION_RUNNING 0 29017 #define MA_PA_OPERATION_DONE 1 29018 #define MA_PA_OPERATION_CANCELLED 2 29019 29020 typedef int ma_pa_sink_state_t; 29021 #define MA_PA_SINK_INVALID_STATE -1 29022 #define MA_PA_SINK_RUNNING 0 29023 #define MA_PA_SINK_IDLE 1 29024 #define MA_PA_SINK_SUSPENDED 2 29025 29026 typedef int ma_pa_source_state_t; 29027 #define MA_PA_SOURCE_INVALID_STATE -1 29028 #define MA_PA_SOURCE_RUNNING 0 29029 #define MA_PA_SOURCE_IDLE 1 29030 #define MA_PA_SOURCE_SUSPENDED 2 29031 29032 typedef int ma_pa_seek_mode_t; 29033 #define MA_PA_SEEK_RELATIVE 0 29034 #define MA_PA_SEEK_ABSOLUTE 1 29035 #define MA_PA_SEEK_RELATIVE_ON_READ 2 29036 #define MA_PA_SEEK_RELATIVE_END 3 29037 29038 typedef int ma_pa_channel_position_t; 29039 #define MA_PA_CHANNEL_POSITION_INVALID -1 29040 #define MA_PA_CHANNEL_POSITION_MONO 0 29041 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 29042 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 29043 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 29044 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 29045 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 29046 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 29047 #define MA_PA_CHANNEL_POSITION_LFE 7 29048 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 29049 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 29050 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 29051 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 29052 #define MA_PA_CHANNEL_POSITION_AUX0 12 29053 #define MA_PA_CHANNEL_POSITION_AUX1 13 29054 #define MA_PA_CHANNEL_POSITION_AUX2 14 29055 #define MA_PA_CHANNEL_POSITION_AUX3 15 29056 #define MA_PA_CHANNEL_POSITION_AUX4 16 29057 #define MA_PA_CHANNEL_POSITION_AUX5 17 29058 #define MA_PA_CHANNEL_POSITION_AUX6 18 29059 #define MA_PA_CHANNEL_POSITION_AUX7 19 29060 #define MA_PA_CHANNEL_POSITION_AUX8 20 29061 #define MA_PA_CHANNEL_POSITION_AUX9 21 29062 #define MA_PA_CHANNEL_POSITION_AUX10 22 29063 #define MA_PA_CHANNEL_POSITION_AUX11 23 29064 #define MA_PA_CHANNEL_POSITION_AUX12 24 29065 #define MA_PA_CHANNEL_POSITION_AUX13 25 29066 #define MA_PA_CHANNEL_POSITION_AUX14 26 29067 #define MA_PA_CHANNEL_POSITION_AUX15 27 29068 #define MA_PA_CHANNEL_POSITION_AUX16 28 29069 #define MA_PA_CHANNEL_POSITION_AUX17 29 29070 #define MA_PA_CHANNEL_POSITION_AUX18 30 29071 #define MA_PA_CHANNEL_POSITION_AUX19 31 29072 #define MA_PA_CHANNEL_POSITION_AUX20 32 29073 #define MA_PA_CHANNEL_POSITION_AUX21 33 29074 #define MA_PA_CHANNEL_POSITION_AUX22 34 29075 #define MA_PA_CHANNEL_POSITION_AUX23 35 29076 #define MA_PA_CHANNEL_POSITION_AUX24 36 29077 #define MA_PA_CHANNEL_POSITION_AUX25 37 29078 #define MA_PA_CHANNEL_POSITION_AUX26 38 29079 #define MA_PA_CHANNEL_POSITION_AUX27 39 29080 #define MA_PA_CHANNEL_POSITION_AUX28 40 29081 #define MA_PA_CHANNEL_POSITION_AUX29 41 29082 #define MA_PA_CHANNEL_POSITION_AUX30 42 29083 #define MA_PA_CHANNEL_POSITION_AUX31 43 29084 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 29085 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 29086 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 29087 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 29088 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 29089 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 29090 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 29091 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT 29092 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT 29093 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER 29094 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE 29095 29096 typedef int ma_pa_channel_map_def_t; 29097 #define MA_PA_CHANNEL_MAP_AIFF 0 29098 #define MA_PA_CHANNEL_MAP_ALSA 1 29099 #define MA_PA_CHANNEL_MAP_AUX 2 29100 #define MA_PA_CHANNEL_MAP_WAVEEX 3 29101 #define MA_PA_CHANNEL_MAP_OSS 4 29102 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF 29103 29104 typedef int ma_pa_sample_format_t; 29105 #define MA_PA_SAMPLE_INVALID -1 29106 #define MA_PA_SAMPLE_U8 0 29107 #define MA_PA_SAMPLE_ALAW 1 29108 #define MA_PA_SAMPLE_ULAW 2 29109 #define MA_PA_SAMPLE_S16LE 3 29110 #define MA_PA_SAMPLE_S16BE 4 29111 #define MA_PA_SAMPLE_FLOAT32LE 5 29112 #define MA_PA_SAMPLE_FLOAT32BE 6 29113 #define MA_PA_SAMPLE_S32LE 7 29114 #define MA_PA_SAMPLE_S32BE 8 29115 #define MA_PA_SAMPLE_S24LE 9 29116 #define MA_PA_SAMPLE_S24BE 10 29117 #define MA_PA_SAMPLE_S24_32LE 11 29118 #define MA_PA_SAMPLE_S24_32BE 12 29119 29120 typedef struct ma_pa_mainloop ma_pa_mainloop; 29121 typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; 29122 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; 29123 typedef struct ma_pa_context ma_pa_context; 29124 typedef struct ma_pa_operation ma_pa_operation; 29125 typedef struct ma_pa_stream ma_pa_stream; 29126 typedef struct ma_pa_spawn_api ma_pa_spawn_api; 29127 29128 typedef struct 29129 { 29130 ma_uint32 maxlength; 29131 ma_uint32 tlength; 29132 ma_uint32 prebuf; 29133 ma_uint32 minreq; 29134 ma_uint32 fragsize; 29135 } ma_pa_buffer_attr; 29136 29137 typedef struct 29138 { 29139 ma_uint8 channels; 29140 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; 29141 } ma_pa_channel_map; 29142 29143 typedef struct 29144 { 29145 ma_uint8 channels; 29146 ma_uint32 values[MA_PA_CHANNELS_MAX]; 29147 } ma_pa_cvolume; 29148 29149 typedef struct 29150 { 29151 ma_pa_sample_format_t format; 29152 ma_uint32 rate; 29153 ma_uint8 channels; 29154 } ma_pa_sample_spec; 29155 29156 typedef struct 29157 { 29158 const char* name; 29159 ma_uint32 index; 29160 const char* description; 29161 ma_pa_sample_spec sample_spec; 29162 ma_pa_channel_map channel_map; 29163 ma_uint32 owner_module; 29164 ma_pa_cvolume volume; 29165 int mute; 29166 ma_uint32 monitor_source; 29167 const char* monitor_source_name; 29168 ma_uint64 latency; 29169 const char* driver; 29170 ma_pa_sink_flags_t flags; 29171 void* proplist; 29172 ma_uint64 configured_latency; 29173 ma_uint32 base_volume; 29174 ma_pa_sink_state_t state; 29175 ma_uint32 n_volume_steps; 29176 ma_uint32 card; 29177 ma_uint32 n_ports; 29178 void** ports; 29179 void* active_port; 29180 ma_uint8 n_formats; 29181 void** formats; 29182 } ma_pa_sink_info; 29183 29184 typedef struct 29185 { 29186 const char *name; 29187 ma_uint32 index; 29188 const char *description; 29189 ma_pa_sample_spec sample_spec; 29190 ma_pa_channel_map channel_map; 29191 ma_uint32 owner_module; 29192 ma_pa_cvolume volume; 29193 int mute; 29194 ma_uint32 monitor_of_sink; 29195 const char *monitor_of_sink_name; 29196 ma_uint64 latency; 29197 const char *driver; 29198 ma_pa_source_flags_t flags; 29199 void* proplist; 29200 ma_uint64 configured_latency; 29201 ma_uint32 base_volume; 29202 ma_pa_source_state_t state; 29203 ma_uint32 n_volume_steps; 29204 ma_uint32 card; 29205 ma_uint32 n_ports; 29206 void** ports; 29207 void* active_port; 29208 ma_uint8 n_formats; 29209 void** formats; 29210 } ma_pa_source_info; 29211 29212 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); 29213 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); 29214 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); 29215 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); 29216 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); 29217 typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); 29218 typedef void (* ma_pa_free_cb_t) (void* p); 29219 #endif 29220 29221 29222 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); 29223 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); 29224 typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); 29225 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); 29226 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); 29227 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); 29228 typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); 29229 typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); 29230 typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); 29231 typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); 29232 typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); 29233 typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); 29234 typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); 29235 typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); 29236 typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); 29237 typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); 29238 typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); 29239 typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); 29240 typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); 29241 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); 29242 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); 29243 typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); 29244 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); 29245 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); 29246 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); 29247 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); 29248 typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); 29249 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); 29250 typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); 29251 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); 29252 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); 29253 typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); 29254 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); 29255 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); 29256 typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); 29257 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); 29258 typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); 29259 typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); 29260 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); 29261 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); 29262 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); 29263 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); 29264 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); 29265 typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); 29266 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); 29267 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); 29268 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); 29269 typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); 29270 typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); 29271 typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); 29272 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); 29273 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); 29274 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); 29275 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); 29276 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); 29277 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); 29278 typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); 29279 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); 29280 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); 29281 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); 29282 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); 29283 29284 typedef struct 29285 { 29286 ma_uint32 count; 29287 ma_uint32 capacity; 29288 ma_device_info* pInfo; 29289 } ma_pulse_device_enum_data; 29290 29291 static ma_result ma_result_from_pulse(int result) 29292 { 29293 if (result < 0) { 29294 return MA_ERROR; 29295 } 29296 29297 switch (result) { 29298 case MA_PA_OK: return MA_SUCCESS; 29299 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; 29300 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; 29301 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; 29302 default: return MA_ERROR; 29303 } 29304 } 29305 29306 #if 0 29307 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) 29308 { 29309 if (ma_is_little_endian()) { 29310 switch (format) { 29311 case ma_format_s16: return MA_PA_SAMPLE_S16LE; 29312 case ma_format_s24: return MA_PA_SAMPLE_S24LE; 29313 case ma_format_s32: return MA_PA_SAMPLE_S32LE; 29314 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; 29315 default: break; 29316 } 29317 } else { 29318 switch (format) { 29319 case ma_format_s16: return MA_PA_SAMPLE_S16BE; 29320 case ma_format_s24: return MA_PA_SAMPLE_S24BE; 29321 case ma_format_s32: return MA_PA_SAMPLE_S32BE; 29322 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; 29323 default: break; 29324 } 29325 } 29326 29327 /* Endian agnostic. */ 29328 switch (format) { 29329 case ma_format_u8: return MA_PA_SAMPLE_U8; 29330 default: return MA_PA_SAMPLE_INVALID; 29331 } 29332 } 29333 #endif 29334 29335 static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) 29336 { 29337 if (ma_is_little_endian()) { 29338 switch (format) { 29339 case MA_PA_SAMPLE_S16LE: return ma_format_s16; 29340 case MA_PA_SAMPLE_S24LE: return ma_format_s24; 29341 case MA_PA_SAMPLE_S32LE: return ma_format_s32; 29342 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; 29343 default: break; 29344 } 29345 } else { 29346 switch (format) { 29347 case MA_PA_SAMPLE_S16BE: return ma_format_s16; 29348 case MA_PA_SAMPLE_S24BE: return ma_format_s24; 29349 case MA_PA_SAMPLE_S32BE: return ma_format_s32; 29350 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; 29351 default: break; 29352 } 29353 } 29354 29355 /* Endian agnostic. */ 29356 switch (format) { 29357 case MA_PA_SAMPLE_U8: return ma_format_u8; 29358 default: return ma_format_unknown; 29359 } 29360 } 29361 29362 static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) 29363 { 29364 switch (position) 29365 { 29366 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; 29367 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; 29368 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; 29369 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; 29370 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; 29371 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; 29372 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; 29373 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; 29374 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; 29375 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; 29376 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; 29377 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; 29378 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; 29379 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; 29380 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; 29381 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; 29382 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; 29383 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; 29384 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; 29385 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; 29386 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; 29387 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; 29388 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; 29389 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; 29390 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; 29391 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; 29392 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; 29393 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; 29394 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; 29395 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; 29396 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; 29397 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; 29398 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; 29399 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; 29400 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; 29401 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; 29402 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; 29403 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; 29404 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; 29405 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; 29406 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; 29407 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; 29408 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; 29409 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; 29410 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; 29411 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; 29412 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; 29413 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; 29414 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; 29415 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; 29416 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; 29417 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; 29418 default: return MA_CHANNEL_NONE; 29419 } 29420 } 29421 29422 #if 0 29423 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) 29424 { 29425 switch (position) 29426 { 29427 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; 29428 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; 29429 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; 29430 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; 29431 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; 29432 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; 29433 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; 29434 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; 29435 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; 29436 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; 29437 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; 29438 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; 29439 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; 29440 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; 29441 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; 29442 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; 29443 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; 29444 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; 29445 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; 29446 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; 29447 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; 29448 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; 29449 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; 29450 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; 29451 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; 29452 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; 29453 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; 29454 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; 29455 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; 29456 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; 29457 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; 29458 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; 29459 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; 29460 default: return (ma_pa_channel_position_t)position; 29461 } 29462 } 29463 #endif 29464 29465 static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) 29466 { 29467 int resultPA; 29468 ma_pa_operation_state_t state; 29469 29470 MA_ASSERT(pContext != NULL); 29471 MA_ASSERT(pOP != NULL); 29472 29473 for (;;) { 29474 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); 29475 if (state != MA_PA_OPERATION_RUNNING) { 29476 break; /* Done. */ 29477 } 29478 29479 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); 29480 if (resultPA < 0) { 29481 return ma_result_from_pulse(resultPA); 29482 } 29483 } 29484 29485 return MA_SUCCESS; 29486 } 29487 29488 static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) 29489 { 29490 ma_result result; 29491 29492 if (pOP == NULL) { 29493 return MA_INVALID_ARGS; 29494 } 29495 29496 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); 29497 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); 29498 29499 return result; 29500 } 29501 29502 static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) 29503 { 29504 int resultPA; 29505 ma_pa_context_state_t state; 29506 29507 for (;;) { 29508 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); 29509 if (state == MA_PA_CONTEXT_READY) { 29510 break; /* Done. */ 29511 } 29512 29513 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { 29514 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); 29515 return MA_ERROR; 29516 } 29517 29518 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); 29519 if (resultPA < 0) { 29520 return ma_result_from_pulse(resultPA); 29521 } 29522 } 29523 29524 /* Should never get here. */ 29525 return MA_SUCCESS; 29526 } 29527 29528 static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) 29529 { 29530 int resultPA; 29531 ma_pa_stream_state_t state; 29532 29533 for (;;) { 29534 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); 29535 if (state == MA_PA_STREAM_READY) { 29536 break; /* Done. */ 29537 } 29538 29539 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { 29540 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); 29541 return MA_ERROR; 29542 } 29543 29544 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); 29545 if (resultPA < 0) { 29546 return ma_result_from_pulse(resultPA); 29547 } 29548 } 29549 29550 return MA_SUCCESS; 29551 } 29552 29553 29554 static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) 29555 { 29556 ma_result result; 29557 ma_ptr pMainLoop; 29558 ma_ptr pPulseContext; 29559 29560 MA_ASSERT(ppMainLoop != NULL); 29561 MA_ASSERT(ppPulseContext != NULL); 29562 29563 /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ 29564 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); 29565 if (pMainLoop == NULL) { 29566 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); 29567 return MA_FAILED_TO_INIT_BACKEND; 29568 } 29569 29570 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); 29571 if (pPulseContext == NULL) { 29572 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); 29573 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); 29574 return MA_FAILED_TO_INIT_BACKEND; 29575 } 29576 29577 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ 29578 result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); 29579 if (result != MA_SUCCESS) { 29580 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); 29581 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); 29582 return result; 29583 } 29584 29585 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ 29586 result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); 29587 if (result != MA_SUCCESS) { 29588 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); 29589 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); 29590 return result; 29591 } 29592 29593 *ppMainLoop = pMainLoop; 29594 *ppPulseContext = pPulseContext; 29595 29596 return MA_SUCCESS; 29597 } 29598 29599 29600 static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) 29601 { 29602 ma_pa_sink_info* pInfoOut; 29603 29604 if (endOfList > 0) { 29605 return; 29606 } 29607 29608 /* 29609 There has been a report that indicates that pInfo can be null which results 29610 in a null pointer dereference below. We'll check for this for safety. 29611 */ 29612 if (pInfo == NULL) { 29613 return; 29614 } 29615 29616 pInfoOut = (ma_pa_sink_info*)pUserData; 29617 MA_ASSERT(pInfoOut != NULL); 29618 29619 *pInfoOut = *pInfo; 29620 29621 (void)pPulseContext; /* Unused. */ 29622 } 29623 29624 static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) 29625 { 29626 ma_pa_source_info* pInfoOut; 29627 29628 if (endOfList > 0) { 29629 return; 29630 } 29631 29632 /* 29633 There has been a report that indicates that pInfo can be null which results 29634 in a null pointer dereference below. We'll check for this for safety. 29635 */ 29636 if (pInfo == NULL) { 29637 return; 29638 } 29639 29640 pInfoOut = (ma_pa_source_info*)pUserData; 29641 MA_ASSERT(pInfoOut != NULL); 29642 29643 *pInfoOut = *pInfo; 29644 29645 (void)pPulseContext; /* Unused. */ 29646 } 29647 29648 #if 0 29649 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) 29650 { 29651 ma_device* pDevice; 29652 29653 if (endOfList > 0) { 29654 return; 29655 } 29656 29657 pDevice = (ma_device*)pUserData; 29658 MA_ASSERT(pDevice != NULL); 29659 29660 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); 29661 29662 (void)pPulseContext; /* Unused. */ 29663 } 29664 29665 static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) 29666 { 29667 ma_device* pDevice; 29668 29669 if (endOfList > 0) { 29670 return; 29671 } 29672 29673 pDevice = (ma_device*)pUserData; 29674 MA_ASSERT(pDevice != NULL); 29675 29676 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); 29677 29678 (void)pPulseContext; /* Unused. */ 29679 } 29680 #endif 29681 29682 static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) 29683 { 29684 ma_pa_operation* pOP; 29685 29686 pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); 29687 if (pOP == NULL) { 29688 return MA_ERROR; 29689 } 29690 29691 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); 29692 } 29693 29694 static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) 29695 { 29696 ma_pa_operation* pOP; 29697 29698 pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); 29699 if (pOP == NULL) { 29700 return MA_ERROR; 29701 } 29702 29703 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); 29704 } 29705 29706 static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) 29707 { 29708 ma_result result; 29709 29710 MA_ASSERT(pContext != NULL); 29711 MA_ASSERT(pIndex != NULL); 29712 29713 if (pIndex != NULL) { 29714 *pIndex = (ma_uint32)-1; 29715 } 29716 29717 if (deviceType == ma_device_type_playback) { 29718 ma_pa_sink_info sinkInfo; 29719 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); 29720 if (result != MA_SUCCESS) { 29721 return result; 29722 } 29723 29724 if (pIndex != NULL) { 29725 *pIndex = sinkInfo.index; 29726 } 29727 } 29728 29729 if (deviceType == ma_device_type_capture) { 29730 ma_pa_source_info sourceInfo; 29731 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); 29732 if (result != MA_SUCCESS) { 29733 return result; 29734 } 29735 29736 if (pIndex != NULL) { 29737 *pIndex = sourceInfo.index; 29738 } 29739 } 29740 29741 return MA_SUCCESS; 29742 } 29743 29744 29745 typedef struct 29746 { 29747 ma_context* pContext; 29748 ma_enum_devices_callback_proc callback; 29749 void* pUserData; 29750 ma_bool32 isTerminated; 29751 ma_uint32 defaultDeviceIndexPlayback; 29752 ma_uint32 defaultDeviceIndexCapture; 29753 } ma_context_enumerate_devices_callback_data__pulse; 29754 29755 static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) 29756 { 29757 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; 29758 ma_device_info deviceInfo; 29759 29760 MA_ASSERT(pData != NULL); 29761 29762 if (endOfList || pData->isTerminated) { 29763 return; 29764 } 29765 29766 MA_ZERO_OBJECT(&deviceInfo); 29767 29768 /* The name from PulseAudio is the ID for miniaudio. */ 29769 if (pSinkInfo->name != NULL) { 29770 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); 29771 } 29772 29773 /* The description from PulseAudio is the name for miniaudio. */ 29774 if (pSinkInfo->description != NULL) { 29775 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); 29776 } 29777 29778 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { 29779 deviceInfo.isDefault = MA_TRUE; 29780 } 29781 29782 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); 29783 29784 (void)pPulseContext; /* Unused. */ 29785 } 29786 29787 static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) 29788 { 29789 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; 29790 ma_device_info deviceInfo; 29791 29792 MA_ASSERT(pData != NULL); 29793 29794 if (endOfList || pData->isTerminated) { 29795 return; 29796 } 29797 29798 MA_ZERO_OBJECT(&deviceInfo); 29799 29800 /* The name from PulseAudio is the ID for miniaudio. */ 29801 if (pSourceInfo->name != NULL) { 29802 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); 29803 } 29804 29805 /* The description from PulseAudio is the name for miniaudio. */ 29806 if (pSourceInfo->description != NULL) { 29807 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); 29808 } 29809 29810 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { 29811 deviceInfo.isDefault = MA_TRUE; 29812 } 29813 29814 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); 29815 29816 (void)pPulseContext; /* Unused. */ 29817 } 29818 29819 static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 29820 { 29821 ma_result result = MA_SUCCESS; 29822 ma_context_enumerate_devices_callback_data__pulse callbackData; 29823 ma_pa_operation* pOP = NULL; 29824 29825 MA_ASSERT(pContext != NULL); 29826 MA_ASSERT(callback != NULL); 29827 29828 callbackData.pContext = pContext; 29829 callbackData.callback = callback; 29830 callbackData.pUserData = pUserData; 29831 callbackData.isTerminated = MA_FALSE; 29832 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; 29833 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; 29834 29835 /* We need to get the index of the default devices. */ 29836 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); 29837 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); 29838 29839 /* Playback. */ 29840 if (!callbackData.isTerminated) { 29841 pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); 29842 if (pOP == NULL) { 29843 result = MA_ERROR; 29844 goto done; 29845 } 29846 29847 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); 29848 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); 29849 29850 if (result != MA_SUCCESS) { 29851 goto done; 29852 } 29853 } 29854 29855 29856 /* Capture. */ 29857 if (!callbackData.isTerminated) { 29858 pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); 29859 if (pOP == NULL) { 29860 result = MA_ERROR; 29861 goto done; 29862 } 29863 29864 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); 29865 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); 29866 29867 if (result != MA_SUCCESS) { 29868 goto done; 29869 } 29870 } 29871 29872 done: 29873 return result; 29874 } 29875 29876 29877 typedef struct 29878 { 29879 ma_device_info* pDeviceInfo; 29880 ma_uint32 defaultDeviceIndex; 29881 ma_bool32 foundDevice; 29882 } ma_context_get_device_info_callback_data__pulse; 29883 29884 static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) 29885 { 29886 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; 29887 29888 if (endOfList > 0) { 29889 return; 29890 } 29891 29892 MA_ASSERT(pData != NULL); 29893 pData->foundDevice = MA_TRUE; 29894 29895 if (pInfo->name != NULL) { 29896 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); 29897 } 29898 29899 if (pInfo->description != NULL) { 29900 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); 29901 } 29902 29903 /* 29904 We're just reporting a single data format here. I think technically PulseAudio might support 29905 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to 29906 report the "native" device format. 29907 */ 29908 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); 29909 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; 29910 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; 29911 pData->pDeviceInfo->nativeDataFormats[0].flags = 0; 29912 pData->pDeviceInfo->nativeDataFormatCount = 1; 29913 29914 if (pData->defaultDeviceIndex == pInfo->index) { 29915 pData->pDeviceInfo->isDefault = MA_TRUE; 29916 } 29917 29918 (void)pPulseContext; /* Unused. */ 29919 } 29920 29921 static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) 29922 { 29923 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; 29924 29925 if (endOfList > 0) { 29926 return; 29927 } 29928 29929 MA_ASSERT(pData != NULL); 29930 pData->foundDevice = MA_TRUE; 29931 29932 if (pInfo->name != NULL) { 29933 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); 29934 } 29935 29936 if (pInfo->description != NULL) { 29937 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); 29938 } 29939 29940 /* 29941 We're just reporting a single data format here. I think technically PulseAudio might support 29942 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to 29943 report the "native" device format. 29944 */ 29945 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); 29946 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; 29947 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; 29948 pData->pDeviceInfo->nativeDataFormats[0].flags = 0; 29949 pData->pDeviceInfo->nativeDataFormatCount = 1; 29950 29951 if (pData->defaultDeviceIndex == pInfo->index) { 29952 pData->pDeviceInfo->isDefault = MA_TRUE; 29953 } 29954 29955 (void)pPulseContext; /* Unused. */ 29956 } 29957 29958 static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 29959 { 29960 ma_result result = MA_SUCCESS; 29961 ma_context_get_device_info_callback_data__pulse callbackData; 29962 ma_pa_operation* pOP = NULL; 29963 const char* pDeviceName = NULL; 29964 29965 MA_ASSERT(pContext != NULL); 29966 29967 callbackData.pDeviceInfo = pDeviceInfo; 29968 callbackData.foundDevice = MA_FALSE; 29969 29970 if (pDeviceID != NULL) { 29971 pDeviceName = pDeviceID->pulse; 29972 } else { 29973 pDeviceName = NULL; 29974 } 29975 29976 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); 29977 29978 if (deviceType == ma_device_type_playback) { 29979 pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); 29980 } else { 29981 pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); 29982 } 29983 29984 if (pOP != NULL) { 29985 ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); 29986 } else { 29987 result = MA_ERROR; 29988 goto done; 29989 } 29990 29991 if (!callbackData.foundDevice) { 29992 result = MA_NO_DEVICE; 29993 goto done; 29994 } 29995 29996 done: 29997 return result; 29998 } 29999 30000 static ma_result ma_device_uninit__pulse(ma_device* pDevice) 30001 { 30002 ma_context* pContext; 30003 30004 MA_ASSERT(pDevice != NULL); 30005 30006 pContext = pDevice->pContext; 30007 MA_ASSERT(pContext != NULL); 30008 30009 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 30010 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30011 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30012 } 30013 30014 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 30015 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30016 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30017 } 30018 30019 if (pDevice->type == ma_device_type_duplex) { 30020 ma_duplex_rb_uninit(&pDevice->duplexRB); 30021 } 30022 30023 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); 30024 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); 30025 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); 30026 30027 return MA_SUCCESS; 30028 } 30029 30030 static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) 30031 { 30032 ma_pa_buffer_attr attr; 30033 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); 30034 attr.tlength = attr.maxlength / periods; 30035 attr.prebuf = (ma_uint32)-1; 30036 attr.minreq = (ma_uint32)-1; 30037 attr.fragsize = attr.maxlength / periods; 30038 30039 return attr; 30040 } 30041 30042 static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) 30043 { 30044 static int g_StreamCounter = 0; 30045 char actualStreamName[256]; 30046 30047 if (pStreamName != NULL) { 30048 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); 30049 } else { 30050 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); 30051 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ 30052 } 30053 g_StreamCounter += 1; 30054 30055 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); 30056 } 30057 30058 30059 static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) 30060 { 30061 ma_device* pDevice = (ma_device*)pUserData; 30062 ma_uint32 bpf; 30063 ma_uint32 deviceState; 30064 ma_uint64 frameCount; 30065 ma_uint64 framesProcessed; 30066 30067 MA_ASSERT(pDevice != NULL); 30068 30069 /* 30070 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio 30071 can fire this callback before the stream has even started. Ridiculous. 30072 */ 30073 deviceState = ma_device_get_state(pDevice); 30074 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { 30075 return; 30076 } 30077 30078 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 30079 MA_ASSERT(bpf > 0); 30080 30081 frameCount = byteCount / bpf; 30082 framesProcessed = 0; 30083 30084 while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { 30085 const void* pMappedPCMFrames; 30086 size_t bytesMapped; 30087 ma_uint64 framesMapped; 30088 30089 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); 30090 if (pulseResult < 0) { 30091 break; /* Failed to map. Abort. */ 30092 } 30093 30094 framesMapped = bytesMapped / bpf; 30095 if (framesMapped > 0) { 30096 if (pMappedPCMFrames != NULL) { 30097 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); 30098 } else { 30099 /* It's a hole. */ 30100 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); 30101 } 30102 30103 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); 30104 if (pulseResult < 0) { 30105 break; /* Failed to drop the buffer. */ 30106 } 30107 30108 framesProcessed += framesMapped; 30109 30110 } else { 30111 /* Nothing was mapped. Just abort. */ 30112 break; 30113 } 30114 } 30115 } 30116 30117 static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) 30118 { 30119 ma_result result = MA_SUCCESS; 30120 ma_uint64 framesProcessed = 0; 30121 size_t bytesMapped; 30122 ma_uint32 bpf; 30123 ma_uint32 deviceState; 30124 30125 MA_ASSERT(pDevice != NULL); 30126 MA_ASSERT(pStream != NULL); 30127 30128 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 30129 MA_ASSERT(bpf > 0); 30130 30131 deviceState = ma_device_get_state(pDevice); 30132 30133 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); 30134 if (bytesMapped != (size_t)-1) { 30135 if (bytesMapped > 0) { 30136 ma_uint64 framesMapped; 30137 void* pMappedPCMFrames; 30138 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); 30139 if (pulseResult < 0) { 30140 result = ma_result_from_pulse(pulseResult); 30141 goto done; 30142 } 30143 30144 framesMapped = bytesMapped / bpf; 30145 30146 if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ 30147 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); 30148 } else { 30149 /* Device is not started. Write silence. */ 30150 ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); 30151 } 30152 30153 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); 30154 if (pulseResult < 0) { 30155 result = ma_result_from_pulse(pulseResult); 30156 goto done; /* Failed to write data to stream. */ 30157 } 30158 30159 framesProcessed += framesMapped; 30160 } else { 30161 result = MA_SUCCESS; /* No data available for writing. */ 30162 goto done; 30163 } 30164 } else { 30165 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ 30166 goto done; 30167 } 30168 30169 done: 30170 if (pFramesProcessed != NULL) { 30171 *pFramesProcessed = framesProcessed; 30172 } 30173 30174 return result; 30175 } 30176 30177 static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) 30178 { 30179 ma_device* pDevice = (ma_device*)pUserData; 30180 ma_uint32 bpf; 30181 ma_uint64 frameCount; 30182 ma_uint64 framesProcessed; 30183 ma_uint32 deviceState; 30184 ma_result result; 30185 30186 MA_ASSERT(pDevice != NULL); 30187 30188 /* 30189 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio 30190 can fire this callback before the stream has even started. Ridiculous. 30191 */ 30192 deviceState = ma_device_get_state(pDevice); 30193 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { 30194 return; 30195 } 30196 30197 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 30198 MA_ASSERT(bpf > 0); 30199 30200 frameCount = byteCount / bpf; 30201 framesProcessed = 0; 30202 30203 while (framesProcessed < frameCount) { 30204 ma_uint64 framesProcessedThisIteration; 30205 30206 /* Don't keep trying to process frames if the device isn't started. */ 30207 deviceState = ma_device_get_state(pDevice); 30208 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { 30209 break; 30210 } 30211 30212 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); 30213 if (result != MA_SUCCESS) { 30214 break; 30215 } 30216 30217 framesProcessed += framesProcessedThisIteration; 30218 } 30219 } 30220 30221 static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) 30222 { 30223 ma_device* pDevice = (ma_device*)pUserData; 30224 int suspended; 30225 30226 (void)pStream; 30227 30228 suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); 30229 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); 30230 30231 if (suspended < 0) { 30232 return; 30233 } 30234 30235 if (suspended == 1) { 30236 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); 30237 ma_device__on_notification_stopped(pDevice); 30238 } else { 30239 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); 30240 ma_device__on_notification_started(pDevice); 30241 } 30242 } 30243 30244 static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) 30245 { 30246 ma_device* pDevice = (ma_device*)pUserData; 30247 30248 (void)pStream; 30249 (void)pUserData; 30250 30251 ma_device__on_notification_rerouted(pDevice); 30252 } 30253 30254 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) 30255 { 30256 /* 30257 There have been reports from users where buffers of < ~20ms result glitches when running through 30258 PipeWire. To work around this we're going to have to use a different default buffer size. 30259 */ 30260 const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; 30261 const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; 30262 30263 MA_ASSERT(nativeSampleRate != 0); 30264 30265 if (pDescriptor->periodSizeInFrames == 0) { 30266 if (pDescriptor->periodSizeInMilliseconds == 0) { 30267 if (performanceProfile == ma_performance_profile_low_latency) { 30268 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); 30269 } else { 30270 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); 30271 } 30272 } else { 30273 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); 30274 } 30275 } else { 30276 return pDescriptor->periodSizeInFrames; 30277 } 30278 } 30279 30280 static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 30281 { 30282 /* 30283 Notes for PulseAudio: 30284 30285 - When both the period size in frames and milliseconds are 0, we default to miniaudio's 30286 default buffer sizes rather than leaving it up to PulseAudio because I don't trust 30287 PulseAudio to give us any kind of reasonable latency by default. 30288 30289 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this 30290 flag, capture mode will just not work properly until you open another PulseAudio app. 30291 */ 30292 30293 ma_result result = MA_SUCCESS; 30294 int error = 0; 30295 const char* devPlayback = NULL; 30296 const char* devCapture = NULL; 30297 ma_format format = ma_format_unknown; 30298 ma_uint32 channels = 0; 30299 ma_uint32 sampleRate = 0; 30300 ma_pa_sink_info sinkInfo; 30301 ma_pa_source_info sourceInfo; 30302 ma_pa_sample_spec ss; 30303 ma_pa_channel_map cmap; 30304 ma_pa_buffer_attr attr; 30305 const ma_pa_sample_spec* pActualSS = NULL; 30306 const ma_pa_buffer_attr* pActualAttr = NULL; 30307 ma_uint32 iChannel; 30308 ma_pa_stream_flags_t streamFlags; 30309 30310 MA_ASSERT(pDevice != NULL); 30311 MA_ZERO_OBJECT(&pDevice->pulse); 30312 30313 if (pConfig->deviceType == ma_device_type_loopback) { 30314 return MA_DEVICE_TYPE_NOT_SUPPORTED; 30315 } 30316 30317 /* No exclusive mode with the PulseAudio backend. */ 30318 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || 30319 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { 30320 return MA_SHARE_MODE_NOT_SUPPORTED; 30321 } 30322 30323 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 30324 if (pDescriptorPlayback->pDeviceID != NULL) { 30325 devPlayback = pDescriptorPlayback->pDeviceID->pulse; 30326 } 30327 30328 format = pDescriptorPlayback->format; 30329 channels = pDescriptorPlayback->channels; 30330 sampleRate = pDescriptorPlayback->sampleRate; 30331 } 30332 30333 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 30334 if (pDescriptorCapture->pDeviceID != NULL) { 30335 devCapture = pDescriptorCapture->pDeviceID->pulse; 30336 } 30337 30338 format = pDescriptorCapture->format; 30339 channels = pDescriptorCapture->channels; 30340 sampleRate = pDescriptorCapture->sampleRate; 30341 } 30342 30343 30344 30345 result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); 30346 if (result != MA_SUCCESS) { 30347 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); 30348 return result; 30349 } 30350 30351 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 30352 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); 30353 if (result != MA_SUCCESS) { 30354 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); 30355 goto on_error0; 30356 } 30357 30358 ss = sourceInfo.sample_spec; 30359 cmap = sourceInfo.channel_map; 30360 30361 /* Use the requested channel count if we have one. */ 30362 if (pDescriptorCapture->channels != 0) { 30363 ss.channels = pDescriptorCapture->channels; 30364 } 30365 30366 /* Use a default channel map. */ 30367 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); 30368 30369 /* Use the requested sample rate if one was specified. */ 30370 if (pDescriptorCapture->sampleRate != 0) { 30371 ss.rate = pDescriptorCapture->sampleRate; 30372 } 30373 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; 30374 30375 if (ma_format_from_pulse(ss.format) == ma_format_unknown) { 30376 if (ma_is_little_endian()) { 30377 ss.format = MA_PA_SAMPLE_FLOAT32LE; 30378 } else { 30379 ss.format = MA_PA_SAMPLE_FLOAT32BE; 30380 } 30381 streamFlags |= MA_PA_STREAM_FIX_FORMAT; 30382 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); 30383 } 30384 if (ss.rate == 0) { 30385 ss.rate = MA_DEFAULT_SAMPLE_RATE; 30386 streamFlags |= MA_PA_STREAM_FIX_RATE; 30387 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); 30388 } 30389 if (ss.channels == 0) { 30390 ss.channels = MA_DEFAULT_CHANNELS; 30391 streamFlags |= MA_PA_STREAM_FIX_CHANNELS; 30392 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); 30393 } 30394 30395 /* We now have enough information to calculate our actual period size in frames. */ 30396 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); 30397 30398 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); 30399 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); 30400 30401 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); 30402 if (pDevice->pulse.pStreamCapture == NULL) { 30403 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); 30404 result = MA_ERROR; 30405 goto on_error0; 30406 } 30407 30408 30409 /* The callback needs to be set before connecting the stream. */ 30410 ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); 30411 30412 /* State callback for checking when the device has been corked. */ 30413 ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); 30414 30415 /* Rerouting notification. */ 30416 ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); 30417 30418 30419 /* Connect after we've got all of our internal state set up. */ 30420 if (devCapture != NULL) { 30421 streamFlags |= MA_PA_STREAM_DONT_MOVE; 30422 } 30423 30424 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); 30425 if (error != MA_PA_OK) { 30426 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); 30427 result = ma_result_from_pulse(error); 30428 goto on_error1; 30429 } 30430 30431 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); 30432 if (result != MA_SUCCESS) { 30433 goto on_error2; 30434 } 30435 30436 30437 /* Internal format. */ 30438 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30439 if (pActualSS != NULL) { 30440 ss = *pActualSS; 30441 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); 30442 } else { 30443 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); 30444 } 30445 30446 pDescriptorCapture->format = ma_format_from_pulse(ss.format); 30447 pDescriptorCapture->channels = ss.channels; 30448 pDescriptorCapture->sampleRate = ss.rate; 30449 30450 if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { 30451 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); 30452 result = MA_ERROR; 30453 goto on_error4; 30454 } 30455 30456 /* Internal channel map. */ 30457 30458 /* 30459 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting 30460 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono 30461 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For 30462 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets 30463 fixed sooner than later. I might remove this hack later. 30464 */ 30465 if (pDescriptorCapture->channels > 2) { 30466 for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { 30467 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); 30468 } 30469 } else { 30470 /* Hack for mono and stereo. */ 30471 if (pDescriptorCapture->channels == 1) { 30472 pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; 30473 } else if (pDescriptorCapture->channels == 2) { 30474 pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; 30475 pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; 30476 } else { 30477 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 30478 } 30479 } 30480 30481 30482 /* Buffer. */ 30483 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30484 if (pActualAttr != NULL) { 30485 attr = *pActualAttr; 30486 } 30487 30488 if (attr.fragsize > 0) { 30489 pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); 30490 } else { 30491 pDescriptorCapture->periodCount = 1; 30492 } 30493 30494 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; 30495 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); 30496 } 30497 30498 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 30499 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); 30500 if (result != MA_SUCCESS) { 30501 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); 30502 goto on_error2; 30503 } 30504 30505 ss = sinkInfo.sample_spec; 30506 cmap = sinkInfo.channel_map; 30507 30508 /* Use the requested channel count if we have one. */ 30509 if (pDescriptorPlayback->channels != 0) { 30510 ss.channels = pDescriptorPlayback->channels; 30511 } 30512 30513 /* Use a default channel map. */ 30514 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); 30515 30516 30517 /* Use the requested sample rate if one was specified. */ 30518 if (pDescriptorPlayback->sampleRate != 0) { 30519 ss.rate = pDescriptorPlayback->sampleRate; 30520 } 30521 30522 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; 30523 if (ma_format_from_pulse(ss.format) == ma_format_unknown) { 30524 if (ma_is_little_endian()) { 30525 ss.format = MA_PA_SAMPLE_FLOAT32LE; 30526 } else { 30527 ss.format = MA_PA_SAMPLE_FLOAT32BE; 30528 } 30529 streamFlags |= MA_PA_STREAM_FIX_FORMAT; 30530 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); 30531 } 30532 if (ss.rate == 0) { 30533 ss.rate = MA_DEFAULT_SAMPLE_RATE; 30534 streamFlags |= MA_PA_STREAM_FIX_RATE; 30535 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); 30536 } 30537 if (ss.channels == 0) { 30538 ss.channels = MA_DEFAULT_CHANNELS; 30539 streamFlags |= MA_PA_STREAM_FIX_CHANNELS; 30540 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); 30541 } 30542 30543 /* We now have enough information to calculate the actual buffer size in frames. */ 30544 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); 30545 30546 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); 30547 30548 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); 30549 30550 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); 30551 if (pDevice->pulse.pStreamPlayback == NULL) { 30552 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); 30553 result = MA_ERROR; 30554 goto on_error2; 30555 } 30556 30557 30558 /* 30559 Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a 30560 device state of ma_device_state_uninitialized. 30561 */ 30562 ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); 30563 30564 /* State callback for checking when the device has been corked. */ 30565 ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); 30566 30567 /* Rerouting notification. */ 30568 ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); 30569 30570 30571 /* Connect after we've got all of our internal state set up. */ 30572 if (devPlayback != NULL) { 30573 streamFlags |= MA_PA_STREAM_DONT_MOVE; 30574 } 30575 30576 error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); 30577 if (error != MA_PA_OK) { 30578 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); 30579 result = ma_result_from_pulse(error); 30580 goto on_error3; 30581 } 30582 30583 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30584 if (result != MA_SUCCESS) { 30585 goto on_error3; 30586 } 30587 30588 30589 /* Internal format. */ 30590 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30591 if (pActualSS != NULL) { 30592 ss = *pActualSS; 30593 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); 30594 } else { 30595 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); 30596 } 30597 30598 pDescriptorPlayback->format = ma_format_from_pulse(ss.format); 30599 pDescriptorPlayback->channels = ss.channels; 30600 pDescriptorPlayback->sampleRate = ss.rate; 30601 30602 if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { 30603 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); 30604 result = MA_ERROR; 30605 goto on_error4; 30606 } 30607 30608 /* Internal channel map. */ 30609 30610 /* 30611 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting 30612 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono 30613 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For 30614 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets 30615 fixed sooner than later. I might remove this hack later. 30616 */ 30617 if (pDescriptorPlayback->channels > 2) { 30618 for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { 30619 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); 30620 } 30621 } else { 30622 /* Hack for mono and stereo. */ 30623 if (pDescriptorPlayback->channels == 1) { 30624 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; 30625 } else if (pDescriptorPlayback->channels == 2) { 30626 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; 30627 pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; 30628 } else { 30629 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 30630 } 30631 } 30632 30633 30634 /* Buffer. */ 30635 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30636 if (pActualAttr != NULL) { 30637 attr = *pActualAttr; 30638 } 30639 30640 if (attr.tlength > 0) { 30641 pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); 30642 } else { 30643 pDescriptorPlayback->periodCount = 1; 30644 } 30645 30646 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; 30647 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); 30648 } 30649 30650 30651 /* 30652 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main 30653 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for 30654 us later on because that will only do it if it's a fully asynchronous backend - i.e. the 30655 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. 30656 */ 30657 if (pConfig->deviceType == ma_device_type_duplex) { 30658 ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; 30659 ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; 30660 ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; 30661 30662 result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); 30663 if (result != MA_SUCCESS) { 30664 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); 30665 goto on_error4; 30666 } 30667 } 30668 30669 return MA_SUCCESS; 30670 30671 30672 on_error4: 30673 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 30674 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30675 } 30676 on_error3: 30677 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 30678 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); 30679 } 30680 on_error2: 30681 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 30682 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30683 } 30684 on_error1: 30685 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 30686 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); 30687 } 30688 on_error0: 30689 return result; 30690 } 30691 30692 30693 static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) 30694 { 30695 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; 30696 MA_ASSERT(pIsSuccessful != NULL); 30697 30698 *pIsSuccessful = (ma_bool32)success; 30699 30700 (void)pStream; /* Unused. */ 30701 } 30702 30703 static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) 30704 { 30705 ma_context* pContext = pDevice->pContext; 30706 ma_bool32 wasSuccessful; 30707 ma_pa_stream* pStream; 30708 ma_pa_operation* pOP; 30709 ma_result result; 30710 30711 /* This should not be called with a duplex device type. */ 30712 if (deviceType == ma_device_type_duplex) { 30713 return MA_INVALID_ARGS; 30714 } 30715 30716 wasSuccessful = MA_FALSE; 30717 30718 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); 30719 MA_ASSERT(pStream != NULL); 30720 30721 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); 30722 if (pOP == NULL) { 30723 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); 30724 return MA_ERROR; 30725 } 30726 30727 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); 30728 if (result != MA_SUCCESS) { 30729 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); 30730 return result; 30731 } 30732 30733 if (!wasSuccessful) { 30734 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); 30735 return MA_ERROR; 30736 } 30737 30738 return MA_SUCCESS; 30739 } 30740 30741 static ma_result ma_device_start__pulse(ma_device* pDevice) 30742 { 30743 ma_result result; 30744 30745 MA_ASSERT(pDevice != NULL); 30746 30747 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 30748 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); 30749 if (result != MA_SUCCESS) { 30750 return result; 30751 } 30752 } 30753 30754 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 30755 /* 30756 We need to fill some data before uncorking. Not doing this will result in the write callback 30757 never getting fired. We're not going to abort if writing fails because I still want the device 30758 to get uncorked. 30759 */ 30760 ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ 30761 30762 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); 30763 if (result != MA_SUCCESS) { 30764 return result; 30765 } 30766 } 30767 30768 return MA_SUCCESS; 30769 } 30770 30771 static ma_result ma_device_stop__pulse(ma_device* pDevice) 30772 { 30773 ma_result result; 30774 30775 MA_ASSERT(pDevice != NULL); 30776 30777 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 30778 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); 30779 if (result != MA_SUCCESS) { 30780 return result; 30781 } 30782 } 30783 30784 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 30785 /* 30786 Ideally we would drain the device here, but there's been cases where PulseAudio seems to be 30787 broken on some systems to the point where no audio processing seems to happen. When this 30788 happens, draining never completes and we get stuck here. For now I'm disabling draining of 30789 the device so we don't just freeze the application. 30790 */ 30791 #if 0 30792 ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); 30793 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); 30794 #endif 30795 30796 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); 30797 if (result != MA_SUCCESS) { 30798 return result; 30799 } 30800 } 30801 30802 return MA_SUCCESS; 30803 } 30804 30805 static ma_result ma_device_data_loop__pulse(ma_device* pDevice) 30806 { 30807 int resultPA; 30808 30809 MA_ASSERT(pDevice != NULL); 30810 30811 /* NOTE: Don't start the device here. It'll be done at a higher level. */ 30812 30813 /* 30814 All data is handled through callbacks. All we need to do is iterate over the main loop and let 30815 the callbacks deal with it. 30816 */ 30817 while (ma_device_get_state(pDevice) == ma_device_state_started) { 30818 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); 30819 if (resultPA < 0) { 30820 break; 30821 } 30822 } 30823 30824 /* NOTE: Don't stop the device here. It'll be done at a higher level. */ 30825 return MA_SUCCESS; 30826 } 30827 30828 static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) 30829 { 30830 MA_ASSERT(pDevice != NULL); 30831 30832 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); 30833 30834 return MA_SUCCESS; 30835 } 30836 30837 static ma_result ma_context_uninit__pulse(ma_context* pContext) 30838 { 30839 MA_ASSERT(pContext != NULL); 30840 MA_ASSERT(pContext->backend == ma_backend_pulseaudio); 30841 30842 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); 30843 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); 30844 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); 30845 30846 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); 30847 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); 30848 30849 #ifndef MA_NO_RUNTIME_LINKING 30850 ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); 30851 #endif 30852 30853 return MA_SUCCESS; 30854 } 30855 30856 static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 30857 { 30858 ma_result result; 30859 #ifndef MA_NO_RUNTIME_LINKING 30860 const char* libpulseNames[] = { 30861 "libpulse.so", 30862 "libpulse.so.0" 30863 }; 30864 size_t i; 30865 30866 for (i = 0; i < ma_countof(libpulseNames); ++i) { 30867 pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); 30868 if (pContext->pulse.pulseSO != NULL) { 30869 break; 30870 } 30871 } 30872 30873 if (pContext->pulse.pulseSO == NULL) { 30874 return MA_NO_BACKEND; 30875 } 30876 30877 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); 30878 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); 30879 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); 30880 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); 30881 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); 30882 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); 30883 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); 30884 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); 30885 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); 30886 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); 30887 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); 30888 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); 30889 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); 30890 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); 30891 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); 30892 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); 30893 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); 30894 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); 30895 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); 30896 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); 30897 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); 30898 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); 30899 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); 30900 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); 30901 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); 30902 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); 30903 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); 30904 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); 30905 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); 30906 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); 30907 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); 30908 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); 30909 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); 30910 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); 30911 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); 30912 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); 30913 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); 30914 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); 30915 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); 30916 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); 30917 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); 30918 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); 30919 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); 30920 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); 30921 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); 30922 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); 30923 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); 30924 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); 30925 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); 30926 pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); 30927 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); 30928 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); 30929 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); 30930 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); 30931 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); 30932 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); 30933 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); 30934 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); 30935 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); 30936 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); 30937 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); 30938 #else 30939 /* This strange assignment system is just for type safety. */ 30940 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; 30941 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; 30942 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; 30943 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; 30944 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; 30945 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; 30946 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; 30947 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; 30948 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; 30949 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; 30950 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; 30951 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; 30952 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; 30953 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; 30954 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; 30955 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; 30956 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; 30957 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; 30958 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; 30959 ma_pa_context_new_proc _pa_context_new = pa_context_new; 30960 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; 30961 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; 30962 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; 30963 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; 30964 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; 30965 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; 30966 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; 30967 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; 30968 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; 30969 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; 30970 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; 30971 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; 30972 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; 30973 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; 30974 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; 30975 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; 30976 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; 30977 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; 30978 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; 30979 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; 30980 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; 30981 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; 30982 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; 30983 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; 30984 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; 30985 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; 30986 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; 30987 ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; 30988 ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; 30989 ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; 30990 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; 30991 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; 30992 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; 30993 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; 30994 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; 30995 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; 30996 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; 30997 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; 30998 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; 30999 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; 31000 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; 31001 31002 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; 31003 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; 31004 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; 31005 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; 31006 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; 31007 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; 31008 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; 31009 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; 31010 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; 31011 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; 31012 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; 31013 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; 31014 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; 31015 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; 31016 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; 31017 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; 31018 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; 31019 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; 31020 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; 31021 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; 31022 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; 31023 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; 31024 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; 31025 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; 31026 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; 31027 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; 31028 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; 31029 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; 31030 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; 31031 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; 31032 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; 31033 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; 31034 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; 31035 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; 31036 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; 31037 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; 31038 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; 31039 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; 31040 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; 31041 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; 31042 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; 31043 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; 31044 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; 31045 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; 31046 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; 31047 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; 31048 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; 31049 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; 31050 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; 31051 pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; 31052 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; 31053 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; 31054 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; 31055 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; 31056 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; 31057 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; 31058 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; 31059 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; 31060 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; 31061 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; 31062 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; 31063 #endif 31064 31065 /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ 31066 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); 31067 if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { 31068 return MA_OUT_OF_MEMORY; 31069 } 31070 31071 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); 31072 if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { 31073 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); 31074 return MA_OUT_OF_MEMORY; 31075 } 31076 31077 result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); 31078 if (result != MA_SUCCESS) { 31079 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); 31080 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); 31081 #ifndef MA_NO_RUNTIME_LINKING 31082 ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); 31083 #endif 31084 return result; 31085 } 31086 31087 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ 31088 pCallbacks->onContextInit = ma_context_init__pulse; 31089 pCallbacks->onContextUninit = ma_context_uninit__pulse; 31090 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; 31091 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; 31092 pCallbacks->onDeviceInit = ma_device_init__pulse; 31093 pCallbacks->onDeviceUninit = ma_device_uninit__pulse; 31094 pCallbacks->onDeviceStart = ma_device_start__pulse; 31095 pCallbacks->onDeviceStop = ma_device_stop__pulse; 31096 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ 31097 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ 31098 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; 31099 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; 31100 31101 return MA_SUCCESS; 31102 } 31103 #endif 31104 31105 31106 /****************************************************************************** 31107 31108 JACK Backend 31109 31110 ******************************************************************************/ 31111 #ifdef MA_HAS_JACK 31112 31113 /* It is assumed jack.h is available when compile-time linking is being used. */ 31114 #ifdef MA_NO_RUNTIME_LINKING 31115 #include <jack/jack.h> 31116 31117 typedef jack_nframes_t ma_jack_nframes_t; 31118 typedef jack_options_t ma_jack_options_t; 31119 typedef jack_status_t ma_jack_status_t; 31120 typedef jack_client_t ma_jack_client_t; 31121 typedef jack_port_t ma_jack_port_t; 31122 typedef JackProcessCallback ma_JackProcessCallback; 31123 typedef JackBufferSizeCallback ma_JackBufferSizeCallback; 31124 typedef JackShutdownCallback ma_JackShutdownCallback; 31125 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE 31126 #define ma_JackNoStartServer JackNoStartServer 31127 #define ma_JackPortIsInput JackPortIsInput 31128 #define ma_JackPortIsOutput JackPortIsOutput 31129 #define ma_JackPortIsPhysical JackPortIsPhysical 31130 #else 31131 typedef ma_uint32 ma_jack_nframes_t; 31132 typedef int ma_jack_options_t; 31133 typedef int ma_jack_status_t; 31134 typedef struct ma_jack_client_t ma_jack_client_t; 31135 typedef struct ma_jack_port_t ma_jack_port_t; 31136 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); 31137 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); 31138 typedef void (* ma_JackShutdownCallback) (void* arg); 31139 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" 31140 #define ma_JackNoStartServer 1 31141 #define ma_JackPortIsInput 1 31142 #define ma_JackPortIsOutput 2 31143 #define ma_JackPortIsPhysical 4 31144 #endif 31145 31146 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); 31147 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); 31148 typedef int (* ma_jack_client_name_size_proc) (void); 31149 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); 31150 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); 31151 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); 31152 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); 31153 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); 31154 typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); 31155 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); 31156 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); 31157 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); 31158 typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); 31159 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); 31160 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); 31161 typedef void (* ma_jack_free_proc) (void* ptr); 31162 31163 static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) 31164 { 31165 size_t maxClientNameSize; 31166 char clientName[256]; 31167 ma_jack_status_t status; 31168 ma_jack_client_t* pClient; 31169 31170 MA_ASSERT(pContext != NULL); 31171 MA_ASSERT(ppClient != NULL); 31172 31173 if (ppClient) { 31174 *ppClient = NULL; 31175 } 31176 31177 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ 31178 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); 31179 31180 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); 31181 if (pClient == NULL) { 31182 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31183 } 31184 31185 if (ppClient) { 31186 *ppClient = pClient; 31187 } 31188 31189 return MA_SUCCESS; 31190 } 31191 31192 31193 static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 31194 { 31195 ma_bool32 cbResult = MA_TRUE; 31196 31197 MA_ASSERT(pContext != NULL); 31198 MA_ASSERT(callback != NULL); 31199 31200 /* Playback. */ 31201 if (cbResult) { 31202 ma_device_info deviceInfo; 31203 MA_ZERO_OBJECT(&deviceInfo); 31204 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 31205 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ 31206 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 31207 } 31208 31209 /* Capture. */ 31210 if (cbResult) { 31211 ma_device_info deviceInfo; 31212 MA_ZERO_OBJECT(&deviceInfo); 31213 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 31214 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ 31215 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 31216 } 31217 31218 (void)cbResult; /* For silencing a static analysis warning. */ 31219 31220 return MA_SUCCESS; 31221 } 31222 31223 static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 31224 { 31225 ma_jack_client_t* pClient; 31226 ma_result result; 31227 const char** ppPorts; 31228 31229 MA_ASSERT(pContext != NULL); 31230 31231 if (pDeviceID != NULL && pDeviceID->jack != 0) { 31232 return MA_NO_DEVICE; /* Don't know the device. */ 31233 } 31234 31235 /* Name / Description */ 31236 if (deviceType == ma_device_type_playback) { 31237 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 31238 } else { 31239 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 31240 } 31241 31242 /* Jack only uses default devices. */ 31243 pDeviceInfo->isDefault = MA_TRUE; 31244 31245 /* Jack only supports f32 and has a specific channel count and sample rate. */ 31246 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; 31247 31248 /* The channel count and sample rate can only be determined by opening the device. */ 31249 result = ma_context_open_client__jack(pContext, &pClient); 31250 if (result != MA_SUCCESS) { 31251 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); 31252 return result; 31253 } 31254 31255 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); 31256 pDeviceInfo->nativeDataFormats[0].channels = 0; 31257 31258 ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); 31259 if (ppPorts == NULL) { 31260 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); 31261 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); 31262 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31263 } 31264 31265 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { 31266 pDeviceInfo->nativeDataFormats[0].channels += 1; 31267 } 31268 31269 pDeviceInfo->nativeDataFormats[0].flags = 0; 31270 pDeviceInfo->nativeDataFormatCount = 1; 31271 31272 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); 31273 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); 31274 31275 (void)pContext; 31276 return MA_SUCCESS; 31277 } 31278 31279 31280 static ma_result ma_device_uninit__jack(ma_device* pDevice) 31281 { 31282 ma_context* pContext; 31283 31284 MA_ASSERT(pDevice != NULL); 31285 31286 pContext = pDevice->pContext; 31287 MA_ASSERT(pContext != NULL); 31288 31289 if (pDevice->jack.pClient != NULL) { 31290 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); 31291 } 31292 31293 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 31294 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); 31295 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); 31296 } 31297 31298 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 31299 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); 31300 ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); 31301 } 31302 31303 return MA_SUCCESS; 31304 } 31305 31306 static void ma_device__jack_shutdown_callback(void* pUserData) 31307 { 31308 /* JACK died. Stop the device. */ 31309 ma_device* pDevice = (ma_device*)pUserData; 31310 MA_ASSERT(pDevice != NULL); 31311 31312 ma_device_stop(pDevice); 31313 } 31314 31315 static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) 31316 { 31317 ma_device* pDevice = (ma_device*)pUserData; 31318 MA_ASSERT(pDevice != NULL); 31319 31320 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 31321 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); 31322 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); 31323 if (pNewBuffer == NULL) { 31324 return MA_OUT_OF_MEMORY; 31325 } 31326 31327 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); 31328 31329 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; 31330 pDevice->playback.internalPeriodSizeInFrames = frameCount; 31331 } 31332 31333 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 31334 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); 31335 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); 31336 if (pNewBuffer == NULL) { 31337 return MA_OUT_OF_MEMORY; 31338 } 31339 31340 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); 31341 31342 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; 31343 pDevice->playback.internalPeriodSizeInFrames = frameCount; 31344 } 31345 31346 return 0; 31347 } 31348 31349 static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) 31350 { 31351 ma_device* pDevice; 31352 ma_context* pContext; 31353 ma_uint32 iChannel; 31354 31355 pDevice = (ma_device*)pUserData; 31356 MA_ASSERT(pDevice != NULL); 31357 31358 pContext = pDevice->pContext; 31359 MA_ASSERT(pContext != NULL); 31360 31361 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 31362 /* Channels need to be interleaved. */ 31363 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { 31364 const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); 31365 if (pSrc != NULL) { 31366 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; 31367 ma_jack_nframes_t iFrame; 31368 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 31369 *pDst = *pSrc; 31370 31371 pDst += pDevice->capture.internalChannels; 31372 pSrc += 1; 31373 } 31374 } 31375 } 31376 31377 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); 31378 } 31379 31380 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 31381 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); 31382 31383 /* Channels need to be deinterleaved. */ 31384 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { 31385 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); 31386 if (pDst != NULL) { 31387 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; 31388 ma_jack_nframes_t iFrame; 31389 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 31390 *pDst = *pSrc; 31391 31392 pDst += 1; 31393 pSrc += pDevice->playback.internalChannels; 31394 } 31395 } 31396 } 31397 } 31398 31399 return 0; 31400 } 31401 31402 static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 31403 { 31404 ma_result result; 31405 ma_uint32 periodSizeInFrames; 31406 31407 MA_ASSERT(pConfig != NULL); 31408 MA_ASSERT(pDevice != NULL); 31409 31410 if (pConfig->deviceType == ma_device_type_loopback) { 31411 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); 31412 return MA_DEVICE_TYPE_NOT_SUPPORTED; 31413 } 31414 31415 /* Only supporting default devices with JACK. */ 31416 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || 31417 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { 31418 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); 31419 return MA_NO_DEVICE; 31420 } 31421 31422 /* No exclusive mode with the JACK backend. */ 31423 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || 31424 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { 31425 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); 31426 return MA_SHARE_MODE_NOT_SUPPORTED; 31427 } 31428 31429 /* Open the client. */ 31430 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); 31431 if (result != MA_SUCCESS) { 31432 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); 31433 return result; 31434 } 31435 31436 /* Callbacks. */ 31437 if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { 31438 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); 31439 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31440 } 31441 if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { 31442 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); 31443 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31444 } 31445 31446 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); 31447 31448 31449 /* The buffer size in frames can change. */ 31450 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); 31451 31452 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 31453 ma_uint32 iPort; 31454 const char** ppPorts; 31455 31456 pDescriptorCapture->format = ma_format_f32; 31457 pDescriptorCapture->channels = 0; 31458 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); 31459 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); 31460 31461 ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); 31462 if (ppPorts == NULL) { 31463 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); 31464 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31465 } 31466 31467 /* Need to count the number of ports first so we can allocate some memory. */ 31468 while (ppPorts[pDescriptorCapture->channels] != NULL) { 31469 pDescriptorCapture->channels += 1; 31470 } 31471 31472 pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); 31473 if (pDevice->jack.ppPortsCapture == NULL) { 31474 return MA_OUT_OF_MEMORY; 31475 } 31476 31477 for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { 31478 char name[64]; 31479 ma_strcpy_s(name, sizeof(name), "capture"); 31480 ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ 31481 31482 pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); 31483 if (pDevice->jack.ppPortsCapture[iPort] == NULL) { 31484 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); 31485 ma_device_uninit__jack(pDevice); 31486 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); 31487 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31488 } 31489 } 31490 31491 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); 31492 31493 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; 31494 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ 31495 31496 pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); 31497 if (pDevice->jack.pIntermediaryBufferCapture == NULL) { 31498 ma_device_uninit__jack(pDevice); 31499 return MA_OUT_OF_MEMORY; 31500 } 31501 } 31502 31503 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 31504 ma_uint32 iPort; 31505 const char** ppPorts; 31506 31507 pDescriptorPlayback->format = ma_format_f32; 31508 pDescriptorPlayback->channels = 0; 31509 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); 31510 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); 31511 31512 ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); 31513 if (ppPorts == NULL) { 31514 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); 31515 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31516 } 31517 31518 /* Need to count the number of ports first so we can allocate some memory. */ 31519 while (ppPorts[pDescriptorPlayback->channels] != NULL) { 31520 pDescriptorPlayback->channels += 1; 31521 } 31522 31523 pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); 31524 if (pDevice->jack.ppPortsPlayback == NULL) { 31525 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); 31526 return MA_OUT_OF_MEMORY; 31527 } 31528 31529 for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { 31530 char name[64]; 31531 ma_strcpy_s(name, sizeof(name), "playback"); 31532 ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ 31533 31534 pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); 31535 if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { 31536 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); 31537 ma_device_uninit__jack(pDevice); 31538 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); 31539 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 31540 } 31541 } 31542 31543 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); 31544 31545 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; 31546 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ 31547 31548 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); 31549 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { 31550 ma_device_uninit__jack(pDevice); 31551 return MA_OUT_OF_MEMORY; 31552 } 31553 } 31554 31555 return MA_SUCCESS; 31556 } 31557 31558 31559 static ma_result ma_device_start__jack(ma_device* pDevice) 31560 { 31561 ma_context* pContext = pDevice->pContext; 31562 int resultJACK; 31563 size_t i; 31564 31565 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); 31566 if (resultJACK != 0) { 31567 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); 31568 return MA_FAILED_TO_START_BACKEND_DEVICE; 31569 } 31570 31571 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 31572 const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); 31573 if (ppServerPorts == NULL) { 31574 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); 31575 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); 31576 return MA_ERROR; 31577 } 31578 31579 for (i = 0; ppServerPorts[i] != NULL; ++i) { 31580 const char* pServerPort = ppServerPorts[i]; 31581 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); 31582 31583 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); 31584 if (resultJACK != 0) { 31585 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); 31586 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); 31587 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); 31588 return MA_ERROR; 31589 } 31590 } 31591 31592 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); 31593 } 31594 31595 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 31596 const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); 31597 if (ppServerPorts == NULL) { 31598 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); 31599 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); 31600 return MA_ERROR; 31601 } 31602 31603 for (i = 0; ppServerPorts[i] != NULL; ++i) { 31604 const char* pServerPort = ppServerPorts[i]; 31605 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); 31606 31607 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); 31608 if (resultJACK != 0) { 31609 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); 31610 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); 31611 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); 31612 return MA_ERROR; 31613 } 31614 } 31615 31616 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); 31617 } 31618 31619 return MA_SUCCESS; 31620 } 31621 31622 static ma_result ma_device_stop__jack(ma_device* pDevice) 31623 { 31624 ma_context* pContext = pDevice->pContext; 31625 31626 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { 31627 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); 31628 return MA_ERROR; 31629 } 31630 31631 ma_device__on_notification_stopped(pDevice); 31632 31633 return MA_SUCCESS; 31634 } 31635 31636 31637 static ma_result ma_context_uninit__jack(ma_context* pContext) 31638 { 31639 MA_ASSERT(pContext != NULL); 31640 MA_ASSERT(pContext->backend == ma_backend_jack); 31641 31642 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); 31643 pContext->jack.pClientName = NULL; 31644 31645 #ifndef MA_NO_RUNTIME_LINKING 31646 ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); 31647 #endif 31648 31649 return MA_SUCCESS; 31650 } 31651 31652 static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 31653 { 31654 #ifndef MA_NO_RUNTIME_LINKING 31655 const char* libjackNames[] = { 31656 #if defined(MA_WIN32) 31657 "libjack.dll", 31658 "libjack64.dll" 31659 #endif 31660 #if defined(MA_UNIX) 31661 "libjack.so", 31662 "libjack.so.0" 31663 #endif 31664 }; 31665 size_t i; 31666 31667 for (i = 0; i < ma_countof(libjackNames); ++i) { 31668 pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); 31669 if (pContext->jack.jackSO != NULL) { 31670 break; 31671 } 31672 } 31673 31674 if (pContext->jack.jackSO == NULL) { 31675 return MA_NO_BACKEND; 31676 } 31677 31678 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); 31679 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); 31680 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); 31681 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); 31682 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); 31683 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); 31684 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); 31685 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); 31686 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); 31687 pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); 31688 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); 31689 pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); 31690 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); 31691 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); 31692 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); 31693 pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); 31694 #else 31695 /* 31696 This strange assignment system is here just to ensure type safety of miniaudio's function pointer 31697 types. If anything differs slightly the compiler should throw a warning. 31698 */ 31699 ma_jack_client_open_proc _jack_client_open = jack_client_open; 31700 ma_jack_client_close_proc _jack_client_close = jack_client_close; 31701 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; 31702 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; 31703 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; 31704 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; 31705 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; 31706 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; 31707 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; 31708 ma_jack_activate_proc _jack_activate = jack_activate; 31709 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; 31710 ma_jack_connect_proc _jack_connect = jack_connect; 31711 ma_jack_port_register_proc _jack_port_register = jack_port_register; 31712 ma_jack_port_name_proc _jack_port_name = jack_port_name; 31713 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; 31714 ma_jack_free_proc _jack_free = jack_free; 31715 31716 pContext->jack.jack_client_open = (ma_proc)_jack_client_open; 31717 pContext->jack.jack_client_close = (ma_proc)_jack_client_close; 31718 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; 31719 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; 31720 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; 31721 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; 31722 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; 31723 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; 31724 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; 31725 pContext->jack.jack_activate = (ma_proc)_jack_activate; 31726 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; 31727 pContext->jack.jack_connect = (ma_proc)_jack_connect; 31728 pContext->jack.jack_port_register = (ma_proc)_jack_port_register; 31729 pContext->jack.jack_port_name = (ma_proc)_jack_port_name; 31730 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; 31731 pContext->jack.jack_free = (ma_proc)_jack_free; 31732 #endif 31733 31734 if (pConfig->jack.pClientName != NULL) { 31735 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); 31736 } 31737 pContext->jack.tryStartServer = pConfig->jack.tryStartServer; 31738 31739 /* 31740 Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting 31741 a temporary client. 31742 */ 31743 { 31744 ma_jack_client_t* pDummyClient; 31745 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); 31746 if (result != MA_SUCCESS) { 31747 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); 31748 #ifndef MA_NO_RUNTIME_LINKING 31749 ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); 31750 #endif 31751 return MA_NO_BACKEND; 31752 } 31753 31754 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); 31755 } 31756 31757 31758 pCallbacks->onContextInit = ma_context_init__jack; 31759 pCallbacks->onContextUninit = ma_context_uninit__jack; 31760 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; 31761 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; 31762 pCallbacks->onDeviceInit = ma_device_init__jack; 31763 pCallbacks->onDeviceUninit = ma_device_uninit__jack; 31764 pCallbacks->onDeviceStart = ma_device_start__jack; 31765 pCallbacks->onDeviceStop = ma_device_stop__jack; 31766 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ 31767 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ 31768 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ 31769 31770 return MA_SUCCESS; 31771 } 31772 #endif /* JACK */ 31773 31774 31775 31776 /****************************************************************************** 31777 31778 Core Audio Backend 31779 31780 References 31781 ========== 31782 - Technical Note TN2091: Device input using the HAL Output Audio Unit 31783 https://developer.apple.com/library/archive/technotes/tn2091/_index.html 31784 31785 ******************************************************************************/ 31786 #ifdef MA_HAS_COREAUDIO 31787 #include <TargetConditionals.h> 31788 31789 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 31790 #define MA_APPLE_MOBILE 31791 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 31792 #define MA_APPLE_TV 31793 #endif 31794 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 31795 #define MA_APPLE_WATCH 31796 #endif 31797 #if __has_feature(objc_arc) 31798 #define MA_BRIDGE_TRANSFER __bridge_transfer 31799 #define MA_BRIDGE_RETAINED __bridge_retained 31800 #else 31801 #define MA_BRIDGE_TRANSFER 31802 #define MA_BRIDGE_RETAINED 31803 #endif 31804 #else 31805 #define MA_APPLE_DESKTOP 31806 #endif 31807 31808 #if defined(MA_APPLE_DESKTOP) 31809 #include <CoreAudio/CoreAudio.h> 31810 #else 31811 #include <AVFoundation/AVFoundation.h> 31812 #endif 31813 31814 #include <AudioToolbox/AudioToolbox.h> 31815 31816 /* CoreFoundation */ 31817 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); 31818 typedef void (* ma_CFRelease_proc)(CFTypeRef cf); 31819 31820 /* CoreAudio */ 31821 #if defined(MA_APPLE_DESKTOP) 31822 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); 31823 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 31824 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); 31825 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); 31826 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); 31827 #endif 31828 31829 /* AudioToolbox */ 31830 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); 31831 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); 31832 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); 31833 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); 31834 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); 31835 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); 31836 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); 31837 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); 31838 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); 31839 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); 31840 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); 31841 31842 31843 #define MA_COREAUDIO_OUTPUT_BUS 0 31844 #define MA_COREAUDIO_INPUT_BUS 1 31845 31846 #if defined(MA_APPLE_DESKTOP) 31847 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); 31848 #endif 31849 31850 /* 31851 Core Audio 31852 31853 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation 31854 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose 31855 needing to figure out how this darn thing works, I'm going to outline a few things here. 31856 31857 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be 31858 able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen 31859 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent 31860 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the 31861 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. 31862 31863 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When 31864 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific 31865 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the 31866 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be 31867 the central APIs for retrieving information about the system and specific devices. 31868 31869 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a 31870 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" 31871 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is 31872 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and 31873 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to 31874 kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. 31875 31876 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size 31877 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property 31878 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the 31879 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of 31880 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. 31881 */ 31882 31883 #if defined(MA_APPLE_MOBILE) 31884 static void ma_device__on_notification_interruption_began(ma_device* pDevice) 31885 { 31886 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); 31887 } 31888 31889 static void ma_device__on_notification_interruption_ended(ma_device* pDevice) 31890 { 31891 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); 31892 } 31893 #endif 31894 31895 static ma_result ma_result_from_OSStatus(OSStatus status) 31896 { 31897 switch (status) 31898 { 31899 case noErr: return MA_SUCCESS; 31900 #if defined(MA_APPLE_DESKTOP) 31901 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; 31902 case kAudioHardwareUnspecifiedError: return MA_ERROR; 31903 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; 31904 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; 31905 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; 31906 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; 31907 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; 31908 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; 31909 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; 31910 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; 31911 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; 31912 #endif 31913 default: return MA_ERROR; 31914 } 31915 } 31916 31917 #if 0 31918 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) 31919 { 31920 switch (bit) 31921 { 31922 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; 31923 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; 31924 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; 31925 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; 31926 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; 31927 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; 31928 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; 31929 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; 31930 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; 31931 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; 31932 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; 31933 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; 31934 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; 31935 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; 31936 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; 31937 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; 31938 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; 31939 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; 31940 default: return MA_CHANNEL_NONE; 31941 } 31942 } 31943 #endif 31944 31945 static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) 31946 { 31947 MA_ASSERT(pDescription != NULL); 31948 MA_ASSERT(pFormatOut != NULL); 31949 31950 *pFormatOut = ma_format_unknown; /* Safety. */ 31951 31952 /* There's a few things miniaudio doesn't support. */ 31953 if (pDescription->mFormatID != kAudioFormatLinearPCM) { 31954 return MA_FORMAT_NOT_SUPPORTED; 31955 } 31956 31957 /* We don't support any non-packed formats that are aligned high. */ 31958 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { 31959 return MA_FORMAT_NOT_SUPPORTED; 31960 } 31961 31962 /* Only supporting native-endian. */ 31963 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { 31964 return MA_FORMAT_NOT_SUPPORTED; 31965 } 31966 31967 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ 31968 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { 31969 return MA_FORMAT_NOT_SUPPORTED; 31970 }*/ 31971 31972 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { 31973 if (pDescription->mBitsPerChannel == 32) { 31974 *pFormatOut = ma_format_f32; 31975 return MA_SUCCESS; 31976 } 31977 } else { 31978 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { 31979 if (pDescription->mBitsPerChannel == 16) { 31980 *pFormatOut = ma_format_s16; 31981 return MA_SUCCESS; 31982 } else if (pDescription->mBitsPerChannel == 24) { 31983 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { 31984 *pFormatOut = ma_format_s24; 31985 return MA_SUCCESS; 31986 } else { 31987 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { 31988 /* TODO: Implement ma_format_s24_32. */ 31989 /**pFormatOut = ma_format_s24_32;*/ 31990 /*return MA_SUCCESS;*/ 31991 return MA_FORMAT_NOT_SUPPORTED; 31992 } 31993 } 31994 } else if (pDescription->mBitsPerChannel == 32) { 31995 *pFormatOut = ma_format_s32; 31996 return MA_SUCCESS; 31997 } 31998 } else { 31999 if (pDescription->mBitsPerChannel == 8) { 32000 *pFormatOut = ma_format_u8; 32001 return MA_SUCCESS; 32002 } 32003 } 32004 } 32005 32006 /* Getting here means the format is not supported. */ 32007 return MA_FORMAT_NOT_SUPPORTED; 32008 } 32009 32010 #if defined(MA_APPLE_DESKTOP) 32011 static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) 32012 { 32013 switch (label) 32014 { 32015 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; 32016 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; 32017 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; 32018 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; 32019 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; 32020 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; 32021 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; 32022 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; 32023 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; 32024 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; 32025 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; 32026 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; 32027 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; 32028 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; 32029 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; 32030 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; 32031 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; 32032 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; 32033 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; 32034 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; 32035 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; 32036 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; 32037 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; 32038 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; 32039 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; 32040 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; 32041 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; 32042 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; 32043 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; 32044 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; 32045 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; 32046 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; 32047 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; 32048 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; 32049 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; 32050 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; 32051 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; 32052 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; 32053 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; 32054 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; 32055 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; 32056 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; 32057 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; 32058 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; 32059 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; 32060 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; 32061 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; 32062 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; 32063 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; 32064 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; 32065 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; 32066 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; 32067 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; 32068 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; 32069 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; 32070 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; 32071 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; 32072 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; 32073 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; 32074 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; 32075 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; 32076 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; 32077 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; 32078 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; 32079 32080 #if 0 /* Introduced in a later version of macOS. */ 32081 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; 32082 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; 32083 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; 32084 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; 32085 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; 32086 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; 32087 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; 32088 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; 32089 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; 32090 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; 32091 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; 32092 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; 32093 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; 32094 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; 32095 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; 32096 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; 32097 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; 32098 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; 32099 #endif 32100 32101 default: return MA_CHANNEL_NONE; 32102 } 32103 } 32104 32105 static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) 32106 { 32107 MA_ASSERT(pChannelLayout != NULL); 32108 32109 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { 32110 UInt32 iChannel; 32111 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { 32112 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); 32113 } 32114 } else 32115 #if 0 32116 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { 32117 /* This is the same kind of system that's used by Windows audio APIs. */ 32118 UInt32 iChannel = 0; 32119 UInt32 iBit; 32120 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; 32121 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { 32122 AudioChannelBitmap bit = bitmap & (1 << iBit); 32123 if (bit != 0) { 32124 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); 32125 } 32126 } 32127 } else 32128 #endif 32129 { 32130 /* 32131 Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should 32132 be updated to determine the mapping based on the tag. 32133 */ 32134 UInt32 channelCount; 32135 32136 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ 32137 if (channelMapCap > 0xFFFFFFFF) { 32138 channelMapCap = 0xFFFFFFFF; 32139 } 32140 32141 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); 32142 32143 switch (pChannelLayout->mChannelLayoutTag) 32144 { 32145 case kAudioChannelLayoutTag_Mono: 32146 case kAudioChannelLayoutTag_Stereo: 32147 case kAudioChannelLayoutTag_StereoHeadphones: 32148 case kAudioChannelLayoutTag_MatrixStereo: 32149 case kAudioChannelLayoutTag_MidSide: 32150 case kAudioChannelLayoutTag_XY: 32151 case kAudioChannelLayoutTag_Binaural: 32152 case kAudioChannelLayoutTag_Ambisonic_B_Format: 32153 { 32154 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); 32155 } break; 32156 32157 case kAudioChannelLayoutTag_Octagonal: 32158 { 32159 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; 32160 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; 32161 } MA_FALLTHROUGH; /* Intentional fallthrough. */ 32162 case kAudioChannelLayoutTag_Hexagonal: 32163 { 32164 pChannelMap[5] = MA_CHANNEL_BACK_CENTER; 32165 } MA_FALLTHROUGH; /* Intentional fallthrough. */ 32166 case kAudioChannelLayoutTag_Pentagonal: 32167 { 32168 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; 32169 } MA_FALLTHROUGH; /* Intentional fallthrough. */ 32170 case kAudioChannelLayoutTag_Quadraphonic: 32171 { 32172 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; 32173 pChannelMap[2] = MA_CHANNEL_BACK_LEFT; 32174 pChannelMap[1] = MA_CHANNEL_RIGHT; 32175 pChannelMap[0] = MA_CHANNEL_LEFT; 32176 } break; 32177 32178 /* TODO: Add support for more tags here. */ 32179 32180 default: 32181 { 32182 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); 32183 } break; 32184 } 32185 } 32186 32187 return MA_SUCCESS; 32188 } 32189 32190 #if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ 32191 (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) 32192 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain 32193 #else 32194 /* kAudioObjectPropertyElementMaster is deprecated. */ 32195 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster 32196 #endif 32197 32198 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ 32199 { 32200 AudioObjectPropertyAddress propAddressDevices; 32201 UInt32 deviceObjectsDataSize; 32202 OSStatus status; 32203 AudioObjectID* pDeviceObjectIDs; 32204 32205 MA_ASSERT(pContext != NULL); 32206 MA_ASSERT(pDeviceCount != NULL); 32207 MA_ASSERT(ppDeviceObjectIDs != NULL); 32208 32209 /* Safety. */ 32210 *pDeviceCount = 0; 32211 *ppDeviceObjectIDs = NULL; 32212 32213 propAddressDevices.mSelector = kAudioHardwarePropertyDevices; 32214 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; 32215 propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32216 32217 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); 32218 if (status != noErr) { 32219 return ma_result_from_OSStatus(status); 32220 } 32221 32222 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); 32223 if (pDeviceObjectIDs == NULL) { 32224 return MA_OUT_OF_MEMORY; 32225 } 32226 32227 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); 32228 if (status != noErr) { 32229 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); 32230 return ma_result_from_OSStatus(status); 32231 } 32232 32233 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); 32234 *ppDeviceObjectIDs = pDeviceObjectIDs; 32235 32236 return MA_SUCCESS; 32237 } 32238 32239 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) 32240 { 32241 AudioObjectPropertyAddress propAddress; 32242 UInt32 dataSize; 32243 OSStatus status; 32244 32245 MA_ASSERT(pContext != NULL); 32246 32247 propAddress.mSelector = kAudioDevicePropertyDeviceUID; 32248 propAddress.mScope = kAudioObjectPropertyScopeGlobal; 32249 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32250 32251 dataSize = sizeof(*pUID); 32252 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); 32253 if (status != noErr) { 32254 return ma_result_from_OSStatus(status); 32255 } 32256 32257 return MA_SUCCESS; 32258 } 32259 32260 static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) 32261 { 32262 CFStringRef uid; 32263 ma_result result; 32264 32265 MA_ASSERT(pContext != NULL); 32266 32267 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); 32268 if (result != MA_SUCCESS) { 32269 return result; 32270 } 32271 32272 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { 32273 return MA_ERROR; 32274 } 32275 32276 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); 32277 return MA_SUCCESS; 32278 } 32279 32280 static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) 32281 { 32282 AudioObjectPropertyAddress propAddress; 32283 CFStringRef deviceName = NULL; 32284 UInt32 dataSize; 32285 OSStatus status; 32286 32287 MA_ASSERT(pContext != NULL); 32288 32289 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; 32290 propAddress.mScope = kAudioObjectPropertyScopeGlobal; 32291 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32292 32293 dataSize = sizeof(deviceName); 32294 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); 32295 if (status != noErr) { 32296 return ma_result_from_OSStatus(status); 32297 } 32298 32299 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { 32300 return MA_ERROR; 32301 } 32302 32303 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); 32304 return MA_SUCCESS; 32305 } 32306 32307 static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) 32308 { 32309 AudioObjectPropertyAddress propAddress; 32310 UInt32 dataSize; 32311 OSStatus status; 32312 AudioBufferList* pBufferList; 32313 ma_bool32 isSupported; 32314 32315 MA_ASSERT(pContext != NULL); 32316 32317 /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ 32318 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; 32319 propAddress.mScope = scope; 32320 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32321 32322 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); 32323 if (status != noErr) { 32324 return MA_FALSE; 32325 } 32326 32327 pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); 32328 if (pBufferList == NULL) { 32329 return MA_FALSE; /* Out of memory. */ 32330 } 32331 32332 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); 32333 if (status != noErr) { 32334 ma_free(pBufferList, &pContext->allocationCallbacks); 32335 return MA_FALSE; 32336 } 32337 32338 isSupported = MA_FALSE; 32339 if (pBufferList->mNumberBuffers > 0) { 32340 isSupported = MA_TRUE; 32341 } 32342 32343 ma_free(pBufferList, &pContext->allocationCallbacks); 32344 return isSupported; 32345 } 32346 32347 static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) 32348 { 32349 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); 32350 } 32351 32352 static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) 32353 { 32354 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); 32355 } 32356 32357 32358 static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ 32359 { 32360 AudioObjectPropertyAddress propAddress; 32361 UInt32 dataSize; 32362 OSStatus status; 32363 AudioStreamRangedDescription* pDescriptions; 32364 32365 MA_ASSERT(pContext != NULL); 32366 MA_ASSERT(pDescriptionCount != NULL); 32367 MA_ASSERT(ppDescriptions != NULL); 32368 32369 /* 32370 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My 32371 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. 32372 */ 32373 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ 32374 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 32375 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32376 32377 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); 32378 if (status != noErr) { 32379 return ma_result_from_OSStatus(status); 32380 } 32381 32382 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); 32383 if (pDescriptions == NULL) { 32384 return MA_OUT_OF_MEMORY; 32385 } 32386 32387 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); 32388 if (status != noErr) { 32389 ma_free(pDescriptions, &pContext->allocationCallbacks); 32390 return ma_result_from_OSStatus(status); 32391 } 32392 32393 *pDescriptionCount = dataSize / sizeof(*pDescriptions); 32394 *ppDescriptions = pDescriptions; 32395 return MA_SUCCESS; 32396 } 32397 32398 32399 static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ 32400 { 32401 AudioObjectPropertyAddress propAddress; 32402 UInt32 dataSize; 32403 OSStatus status; 32404 AudioChannelLayout* pChannelLayout; 32405 32406 MA_ASSERT(pContext != NULL); 32407 MA_ASSERT(ppChannelLayout != NULL); 32408 32409 *ppChannelLayout = NULL; /* Safety. */ 32410 32411 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; 32412 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 32413 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32414 32415 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); 32416 if (status != noErr) { 32417 return ma_result_from_OSStatus(status); 32418 } 32419 32420 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); 32421 if (pChannelLayout == NULL) { 32422 return MA_OUT_OF_MEMORY; 32423 } 32424 32425 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); 32426 if (status != noErr) { 32427 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32428 return ma_result_from_OSStatus(status); 32429 } 32430 32431 *ppChannelLayout = pChannelLayout; 32432 return MA_SUCCESS; 32433 } 32434 32435 static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) 32436 { 32437 AudioChannelLayout* pChannelLayout; 32438 ma_result result; 32439 32440 MA_ASSERT(pContext != NULL); 32441 MA_ASSERT(pChannelCount != NULL); 32442 32443 *pChannelCount = 0; /* Safety. */ 32444 32445 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); 32446 if (result != MA_SUCCESS) { 32447 return result; 32448 } 32449 32450 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { 32451 *pChannelCount = pChannelLayout->mNumberChannelDescriptions; 32452 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { 32453 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); 32454 } else { 32455 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); 32456 } 32457 32458 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32459 return MA_SUCCESS; 32460 } 32461 32462 #if 0 32463 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) 32464 { 32465 AudioChannelLayout* pChannelLayout; 32466 ma_result result; 32467 32468 MA_ASSERT(pContext != NULL); 32469 32470 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); 32471 if (result != MA_SUCCESS) { 32472 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ 32473 } 32474 32475 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); 32476 if (result != MA_SUCCESS) { 32477 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32478 return result; 32479 } 32480 32481 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32482 return result; 32483 } 32484 #endif 32485 32486 static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ 32487 { 32488 AudioObjectPropertyAddress propAddress; 32489 UInt32 dataSize; 32490 OSStatus status; 32491 AudioValueRange* pSampleRateRanges; 32492 32493 MA_ASSERT(pContext != NULL); 32494 MA_ASSERT(pSampleRateRangesCount != NULL); 32495 MA_ASSERT(ppSampleRateRanges != NULL); 32496 32497 /* Safety. */ 32498 *pSampleRateRangesCount = 0; 32499 *ppSampleRateRanges = NULL; 32500 32501 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; 32502 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 32503 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32504 32505 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); 32506 if (status != noErr) { 32507 return ma_result_from_OSStatus(status); 32508 } 32509 32510 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); 32511 if (pSampleRateRanges == NULL) { 32512 return MA_OUT_OF_MEMORY; 32513 } 32514 32515 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); 32516 if (status != noErr) { 32517 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32518 return ma_result_from_OSStatus(status); 32519 } 32520 32521 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); 32522 *ppSampleRateRanges = pSampleRateRanges; 32523 return MA_SUCCESS; 32524 } 32525 32526 #if 0 32527 static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) 32528 { 32529 UInt32 sampleRateRangeCount; 32530 AudioValueRange* pSampleRateRanges; 32531 ma_result result; 32532 32533 MA_ASSERT(pContext != NULL); 32534 MA_ASSERT(pSampleRateOut != NULL); 32535 32536 *pSampleRateOut = 0; /* Safety. */ 32537 32538 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); 32539 if (result != MA_SUCCESS) { 32540 return result; 32541 } 32542 32543 if (sampleRateRangeCount == 0) { 32544 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32545 return MA_ERROR; /* Should never hit this case should we? */ 32546 } 32547 32548 if (sampleRateIn == 0) { 32549 /* Search in order of miniaudio's preferred priority. */ 32550 UInt32 iMALSampleRate; 32551 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { 32552 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; 32553 UInt32 iCASampleRate; 32554 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { 32555 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; 32556 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { 32557 *pSampleRateOut = malSampleRate; 32558 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32559 return MA_SUCCESS; 32560 } 32561 } 32562 } 32563 32564 /* 32565 If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this 32566 case we just fall back to the first one reported by Core Audio. 32567 */ 32568 MA_ASSERT(sampleRateRangeCount > 0); 32569 32570 *pSampleRateOut = pSampleRateRanges[0].mMinimum; 32571 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32572 return MA_SUCCESS; 32573 } else { 32574 /* Find the closest match to this sample rate. */ 32575 UInt32 currentAbsoluteDifference = INT32_MAX; 32576 UInt32 iCurrentClosestRange = (UInt32)-1; 32577 UInt32 iRange; 32578 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { 32579 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { 32580 *pSampleRateOut = sampleRateIn; 32581 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32582 return MA_SUCCESS; 32583 } else { 32584 UInt32 absoluteDifference; 32585 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { 32586 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; 32587 } else { 32588 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; 32589 } 32590 32591 if (currentAbsoluteDifference > absoluteDifference) { 32592 currentAbsoluteDifference = absoluteDifference; 32593 iCurrentClosestRange = iRange; 32594 } 32595 } 32596 } 32597 32598 MA_ASSERT(iCurrentClosestRange != (UInt32)-1); 32599 32600 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; 32601 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 32602 return MA_SUCCESS; 32603 } 32604 32605 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ 32606 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ 32607 /*return MA_ERROR;*/ 32608 } 32609 #endif 32610 32611 static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) 32612 { 32613 AudioObjectPropertyAddress propAddress; 32614 AudioValueRange bufferSizeRange; 32615 UInt32 dataSize; 32616 OSStatus status; 32617 32618 MA_ASSERT(pContext != NULL); 32619 MA_ASSERT(pBufferSizeInFramesOut != NULL); 32620 32621 *pBufferSizeInFramesOut = 0; /* Safety. */ 32622 32623 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; 32624 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 32625 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32626 32627 dataSize = sizeof(bufferSizeRange); 32628 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); 32629 if (status != noErr) { 32630 return ma_result_from_OSStatus(status); 32631 } 32632 32633 /* This is just a clamp. */ 32634 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { 32635 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; 32636 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { 32637 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; 32638 } else { 32639 *pBufferSizeInFramesOut = bufferSizeInFramesIn; 32640 } 32641 32642 return MA_SUCCESS; 32643 } 32644 32645 static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) 32646 { 32647 ma_result result; 32648 ma_uint32 chosenBufferSizeInFrames; 32649 AudioObjectPropertyAddress propAddress; 32650 UInt32 dataSize; 32651 OSStatus status; 32652 32653 MA_ASSERT(pContext != NULL); 32654 32655 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); 32656 if (result != MA_SUCCESS) { 32657 return result; 32658 } 32659 32660 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ 32661 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; 32662 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 32663 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32664 32665 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); 32666 32667 /* Get the actual size of the buffer. */ 32668 dataSize = sizeof(*pPeriodSizeInOut); 32669 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); 32670 if (status != noErr) { 32671 return ma_result_from_OSStatus(status); 32672 } 32673 32674 *pPeriodSizeInOut = chosenBufferSizeInFrames; 32675 return MA_SUCCESS; 32676 } 32677 32678 static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) 32679 { 32680 AudioObjectPropertyAddress propAddressDefaultDevice; 32681 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); 32682 AudioObjectID defaultDeviceObjectID; 32683 OSStatus status; 32684 32685 MA_ASSERT(pContext != NULL); 32686 MA_ASSERT(pDeviceObjectID != NULL); 32687 32688 /* Safety. */ 32689 *pDeviceObjectID = 0; 32690 32691 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; 32692 propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 32693 if (deviceType == ma_device_type_playback) { 32694 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; 32695 } else { 32696 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; 32697 } 32698 32699 defaultDeviceObjectIDSize = sizeof(AudioObjectID); 32700 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); 32701 if (status == noErr) { 32702 *pDeviceObjectID = defaultDeviceObjectID; 32703 return MA_SUCCESS; 32704 } 32705 32706 /* If we get here it means we couldn't find the device. */ 32707 return MA_NO_DEVICE; 32708 } 32709 32710 static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) 32711 { 32712 MA_ASSERT(pContext != NULL); 32713 MA_ASSERT(pDeviceObjectID != NULL); 32714 32715 /* Safety. */ 32716 *pDeviceObjectID = 0; 32717 32718 if (pDeviceID == NULL) { 32719 /* Default device. */ 32720 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); 32721 } else { 32722 /* Explicit device. */ 32723 UInt32 deviceCount; 32724 AudioObjectID* pDeviceObjectIDs; 32725 ma_result result; 32726 UInt32 iDevice; 32727 32728 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); 32729 if (result != MA_SUCCESS) { 32730 return result; 32731 } 32732 32733 for (iDevice = 0; iDevice < deviceCount; ++iDevice) { 32734 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; 32735 32736 char uid[256]; 32737 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { 32738 continue; 32739 } 32740 32741 if (deviceType == ma_device_type_playback) { 32742 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { 32743 if (strcmp(uid, pDeviceID->coreaudio) == 0) { 32744 *pDeviceObjectID = deviceObjectID; 32745 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); 32746 return MA_SUCCESS; 32747 } 32748 } 32749 } else { 32750 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { 32751 if (strcmp(uid, pDeviceID->coreaudio) == 0) { 32752 *pDeviceObjectID = deviceObjectID; 32753 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); 32754 return MA_SUCCESS; 32755 } 32756 } 32757 } 32758 } 32759 32760 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); 32761 } 32762 32763 /* If we get here it means we couldn't find the device. */ 32764 return MA_NO_DEVICE; 32765 } 32766 32767 32768 static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) 32769 { 32770 UInt32 deviceFormatDescriptionCount; 32771 AudioStreamRangedDescription* pDeviceFormatDescriptions; 32772 ma_result result; 32773 ma_uint32 desiredSampleRate; 32774 ma_uint32 desiredChannelCount; 32775 ma_format desiredFormat; 32776 AudioStreamBasicDescription bestDeviceFormatSoFar; 32777 ma_bool32 hasSupportedFormat; 32778 UInt32 iFormat; 32779 32780 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); 32781 if (result != MA_SUCCESS) { 32782 return result; 32783 } 32784 32785 desiredSampleRate = sampleRate; 32786 if (desiredSampleRate == 0) { 32787 desiredSampleRate = pOrigFormat->mSampleRate; 32788 } 32789 32790 desiredChannelCount = channels; 32791 if (desiredChannelCount == 0) { 32792 desiredChannelCount = pOrigFormat->mChannelsPerFrame; 32793 } 32794 32795 desiredFormat = format; 32796 if (desiredFormat == ma_format_unknown) { 32797 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); 32798 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { 32799 desiredFormat = g_maFormatPriorities[0]; 32800 } 32801 } 32802 32803 /* 32804 If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next 32805 loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. 32806 */ 32807 MA_ZERO_OBJECT(&bestDeviceFormatSoFar); 32808 32809 hasSupportedFormat = MA_FALSE; 32810 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { 32811 ma_format formatFromDescription; 32812 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); 32813 if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { 32814 hasSupportedFormat = MA_TRUE; 32815 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; 32816 break; 32817 } 32818 } 32819 32820 if (!hasSupportedFormat) { 32821 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); 32822 return MA_FORMAT_NOT_SUPPORTED; 32823 } 32824 32825 32826 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { 32827 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; 32828 ma_format thisSampleFormat; 32829 ma_result formatResult; 32830 ma_format bestSampleFormatSoFar; 32831 32832 /* If the format is not supported by miniaudio we need to skip this one entirely. */ 32833 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); 32834 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { 32835 continue; /* The format is not supported by miniaudio. Skip. */ 32836 } 32837 32838 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); 32839 32840 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ 32841 if (thisDeviceFormat.mSampleRate != desiredSampleRate) { 32842 /* 32843 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format 32844 so far has an equal sample rate we can just ignore this one. 32845 */ 32846 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { 32847 continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ 32848 } else { 32849 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ 32850 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { 32851 /* This format has a different sample rate _and_ a different channel count. */ 32852 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { 32853 continue; /* No change to the best format. */ 32854 } else { 32855 /* 32856 Both this format and the best so far have different sample rates and different channel counts. Whichever has the 32857 best format is the new best. 32858 */ 32859 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { 32860 bestDeviceFormatSoFar = thisDeviceFormat; 32861 continue; 32862 } else { 32863 continue; /* No change to the best format. */ 32864 } 32865 } 32866 } else { 32867 /* This format has a different sample rate but the desired channel count. */ 32868 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { 32869 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ 32870 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { 32871 bestDeviceFormatSoFar = thisDeviceFormat; 32872 continue; 32873 } else { 32874 continue; /* No change to the best format for now. */ 32875 } 32876 } else { 32877 /* This format has the desired channel count, but the best so far does not. We have a new best. */ 32878 bestDeviceFormatSoFar = thisDeviceFormat; 32879 continue; 32880 } 32881 } 32882 } 32883 } else { 32884 /* 32885 The sample rates match which makes this format a very high priority contender. If the best format so far has a different 32886 sample rate it needs to be replaced with this one. 32887 */ 32888 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { 32889 bestDeviceFormatSoFar = thisDeviceFormat; 32890 continue; 32891 } else { 32892 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ 32893 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { 32894 /* 32895 In this case this format has the same channel count as what the client is requesting. If the best format so far has 32896 a different count, this one becomes the new best. 32897 */ 32898 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { 32899 bestDeviceFormatSoFar = thisDeviceFormat; 32900 continue; 32901 } else { 32902 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ 32903 if (thisSampleFormat == desiredFormat) { 32904 bestDeviceFormatSoFar = thisDeviceFormat; 32905 break; /* Found the exact match. */ 32906 } else { 32907 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ 32908 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { 32909 bestDeviceFormatSoFar = thisDeviceFormat; 32910 continue; 32911 } else { 32912 continue; /* No change to the best format for now. */ 32913 } 32914 } 32915 } 32916 } else { 32917 /* 32918 In this case the channel count is different to what the client has requested. If the best so far has the same channel 32919 count as the requested count then it remains the best. 32920 */ 32921 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { 32922 continue; 32923 } else { 32924 /* 32925 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about 32926 the same priority, but we need to compare the format now. 32927 */ 32928 if (thisSampleFormat == bestSampleFormatSoFar) { 32929 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { 32930 bestDeviceFormatSoFar = thisDeviceFormat; 32931 continue; 32932 } else { 32933 continue; /* No change to the best format for now. */ 32934 } 32935 } 32936 } 32937 } 32938 } 32939 } 32940 } 32941 32942 *pFormat = bestDeviceFormatSoFar; 32943 32944 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); 32945 return MA_SUCCESS; 32946 } 32947 32948 static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) 32949 { 32950 AudioUnitScope deviceScope; 32951 AudioUnitElement deviceBus; 32952 UInt32 channelLayoutSize; 32953 OSStatus status; 32954 AudioChannelLayout* pChannelLayout; 32955 ma_result result; 32956 32957 MA_ASSERT(pContext != NULL); 32958 32959 if (deviceType == ma_device_type_playback) { 32960 deviceScope = kAudioUnitScope_Input; 32961 deviceBus = MA_COREAUDIO_OUTPUT_BUS; 32962 } else { 32963 deviceScope = kAudioUnitScope_Output; 32964 deviceBus = MA_COREAUDIO_INPUT_BUS; 32965 } 32966 32967 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); 32968 if (status != noErr) { 32969 return ma_result_from_OSStatus(status); 32970 } 32971 32972 pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); 32973 if (pChannelLayout == NULL) { 32974 return MA_OUT_OF_MEMORY; 32975 } 32976 32977 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); 32978 if (status != noErr) { 32979 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32980 return ma_result_from_OSStatus(status); 32981 } 32982 32983 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); 32984 if (result != MA_SUCCESS) { 32985 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32986 return result; 32987 } 32988 32989 ma_free(pChannelLayout, &pContext->allocationCallbacks); 32990 return MA_SUCCESS; 32991 } 32992 #endif /* MA_APPLE_DESKTOP */ 32993 32994 32995 #if !defined(MA_APPLE_DESKTOP) 32996 static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) 32997 { 32998 MA_ZERO_OBJECT(pInfo); 32999 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); 33000 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); 33001 } 33002 #endif 33003 33004 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 33005 { 33006 #if defined(MA_APPLE_DESKTOP) 33007 UInt32 deviceCount; 33008 AudioObjectID* pDeviceObjectIDs; 33009 AudioObjectID defaultDeviceObjectIDPlayback; 33010 AudioObjectID defaultDeviceObjectIDCapture; 33011 ma_result result; 33012 UInt32 iDevice; 33013 33014 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ 33015 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ 33016 33017 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); 33018 if (result != MA_SUCCESS) { 33019 return result; 33020 } 33021 33022 for (iDevice = 0; iDevice < deviceCount; ++iDevice) { 33023 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; 33024 ma_device_info info; 33025 33026 MA_ZERO_OBJECT(&info); 33027 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { 33028 continue; 33029 } 33030 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { 33031 continue; 33032 } 33033 33034 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { 33035 if (deviceObjectID == defaultDeviceObjectIDPlayback) { 33036 info.isDefault = MA_TRUE; 33037 } 33038 33039 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { 33040 break; 33041 } 33042 } 33043 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { 33044 if (deviceObjectID == defaultDeviceObjectIDCapture) { 33045 info.isDefault = MA_TRUE; 33046 } 33047 33048 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { 33049 break; 33050 } 33051 } 33052 } 33053 33054 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); 33055 #else 33056 ma_device_info info; 33057 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; 33058 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; 33059 33060 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { 33061 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); 33062 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { 33063 return MA_SUCCESS; 33064 } 33065 } 33066 33067 for (AVAudioSessionPortDescription* pPortDesc in pInputs) { 33068 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); 33069 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { 33070 return MA_SUCCESS; 33071 } 33072 } 33073 #endif 33074 33075 return MA_SUCCESS; 33076 } 33077 33078 static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 33079 { 33080 ma_result result; 33081 33082 MA_ASSERT(pContext != NULL); 33083 33084 #if defined(MA_APPLE_DESKTOP) 33085 /* Desktop */ 33086 { 33087 AudioObjectID deviceObjectID; 33088 AudioObjectID defaultDeviceObjectID; 33089 UInt32 streamDescriptionCount; 33090 AudioStreamRangedDescription* pStreamDescriptions; 33091 UInt32 iStreamDescription; 33092 UInt32 sampleRateRangeCount; 33093 AudioValueRange* pSampleRateRanges; 33094 33095 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ 33096 33097 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); 33098 if (result != MA_SUCCESS) { 33099 return result; 33100 } 33101 33102 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); 33103 if (result != MA_SUCCESS) { 33104 return result; 33105 } 33106 33107 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); 33108 if (result != MA_SUCCESS) { 33109 return result; 33110 } 33111 33112 if (deviceObjectID == defaultDeviceObjectID) { 33113 pDeviceInfo->isDefault = MA_TRUE; 33114 } 33115 33116 /* 33117 There could be a large number of permutations here. Fortunately there is only a single channel count 33118 being reported which reduces this quite a bit. For sample rates we're only reporting those that are 33119 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into 33120 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen 33121 if some driver performs software data conversion and therefore reports every possible format and 33122 sample rate. 33123 */ 33124 pDeviceInfo->nativeDataFormatCount = 0; 33125 33126 /* Formats. */ 33127 { 33128 ma_format uniqueFormats[ma_format_count]; 33129 ma_uint32 uniqueFormatCount = 0; 33130 ma_uint32 channels; 33131 33132 /* Channels. */ 33133 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); 33134 if (result != MA_SUCCESS) { 33135 return result; 33136 } 33137 33138 /* Formats. */ 33139 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); 33140 if (result != MA_SUCCESS) { 33141 return result; 33142 } 33143 33144 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { 33145 ma_format format; 33146 ma_bool32 hasFormatBeenHandled = MA_FALSE; 33147 ma_uint32 iOutputFormat; 33148 ma_uint32 iSampleRate; 33149 33150 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); 33151 if (result != MA_SUCCESS) { 33152 continue; 33153 } 33154 33155 MA_ASSERT(format != ma_format_unknown); 33156 33157 /* Make sure the format isn't already in the output list. */ 33158 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { 33159 if (uniqueFormats[iOutputFormat] == format) { 33160 hasFormatBeenHandled = MA_TRUE; 33161 break; 33162 } 33163 } 33164 33165 /* If we've already handled this format just skip it. */ 33166 if (hasFormatBeenHandled) { 33167 continue; 33168 } 33169 33170 uniqueFormats[uniqueFormatCount] = format; 33171 uniqueFormatCount += 1; 33172 33173 /* Sample Rates */ 33174 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); 33175 if (result != MA_SUCCESS) { 33176 return result; 33177 } 33178 33179 /* 33180 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are 33181 between this range. 33182 */ 33183 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { 33184 ma_uint32 iStandardSampleRate; 33185 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { 33186 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; 33187 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { 33188 /* We have a new data format. Add it to the list. */ 33189 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; 33190 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 33191 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; 33192 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; 33193 pDeviceInfo->nativeDataFormatCount += 1; 33194 33195 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { 33196 break; /* No more room for any more formats. */ 33197 } 33198 } 33199 } 33200 } 33201 33202 ma_free(pSampleRateRanges, &pContext->allocationCallbacks); 33203 33204 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { 33205 break; /* No more room for any more formats. */ 33206 } 33207 } 33208 33209 ma_free(pStreamDescriptions, &pContext->allocationCallbacks); 33210 } 33211 } 33212 #else 33213 /* Mobile */ 33214 { 33215 AudioComponentDescription desc; 33216 AudioComponent component; 33217 AudioUnit audioUnit; 33218 OSStatus status; 33219 AudioUnitScope formatScope; 33220 AudioUnitElement formatElement; 33221 AudioStreamBasicDescription bestFormat; 33222 UInt32 propSize; 33223 33224 /* We want to ensure we use a consistent device name to device enumeration. */ 33225 if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { 33226 ma_bool32 found = MA_FALSE; 33227 if (deviceType == ma_device_type_playback) { 33228 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; 33229 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { 33230 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { 33231 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); 33232 found = MA_TRUE; 33233 break; 33234 } 33235 } 33236 } else { 33237 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; 33238 for (AVAudioSessionPortDescription* pPortDesc in pInputs) { 33239 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { 33240 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); 33241 found = MA_TRUE; 33242 break; 33243 } 33244 } 33245 } 33246 33247 if (!found) { 33248 return MA_DOES_NOT_EXIST; 33249 } 33250 } else { 33251 if (deviceType == ma_device_type_playback) { 33252 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 33253 } else { 33254 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 33255 } 33256 } 33257 33258 33259 /* 33260 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is 33261 reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to 33262 retrieve from the AVAudioSession shared instance. 33263 */ 33264 desc.componentType = kAudioUnitType_Output; 33265 desc.componentSubType = kAudioUnitSubType_RemoteIO; 33266 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 33267 desc.componentFlags = 0; 33268 desc.componentFlagsMask = 0; 33269 33270 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); 33271 if (component == NULL) { 33272 return MA_FAILED_TO_INIT_BACKEND; 33273 } 33274 33275 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); 33276 if (status != noErr) { 33277 return ma_result_from_OSStatus(status); 33278 } 33279 33280 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; 33281 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; 33282 33283 propSize = sizeof(bestFormat); 33284 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); 33285 if (status != noErr) { 33286 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); 33287 return ma_result_from_OSStatus(status); 33288 } 33289 33290 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); 33291 audioUnit = NULL; 33292 33293 /* Only a single format is being reported for iOS. */ 33294 pDeviceInfo->nativeDataFormatCount = 1; 33295 33296 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); 33297 if (result != MA_SUCCESS) { 33298 return result; 33299 } 33300 33301 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; 33302 33303 /* 33304 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do 33305 this we just get the shared instance and inspect. 33306 */ 33307 @autoreleasepool { 33308 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; 33309 MA_ASSERT(pAudioSession != NULL); 33310 33311 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; 33312 } 33313 } 33314 #endif 33315 33316 (void)pDeviceInfo; /* Unused. */ 33317 return MA_SUCCESS; 33318 } 33319 33320 static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) 33321 { 33322 AudioBufferList* pBufferList; 33323 UInt32 audioBufferSizeInBytes; 33324 size_t allocationSize; 33325 33326 MA_ASSERT(sizeInFrames > 0); 33327 MA_ASSERT(format != ma_format_unknown); 33328 MA_ASSERT(channels > 0); 33329 33330 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ 33331 if (layout == ma_stream_layout_interleaved) { 33332 /* Interleaved case. This is the simple case because we just have one buffer. */ 33333 allocationSize += sizeof(AudioBuffer) * 1; 33334 } else { 33335 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ 33336 allocationSize += sizeof(AudioBuffer) * channels; 33337 } 33338 33339 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); 33340 33341 pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); 33342 if (pBufferList == NULL) { 33343 return NULL; 33344 } 33345 33346 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); 33347 33348 if (layout == ma_stream_layout_interleaved) { 33349 pBufferList->mNumberBuffers = 1; 33350 pBufferList->mBuffers[0].mNumberChannels = channels; 33351 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; 33352 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); 33353 } else { 33354 ma_uint32 iBuffer; 33355 pBufferList->mNumberBuffers = channels; 33356 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { 33357 pBufferList->mBuffers[iBuffer].mNumberChannels = 1; 33358 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; 33359 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); 33360 } 33361 } 33362 33363 return pBufferList; 33364 } 33365 33366 static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) 33367 { 33368 MA_ASSERT(pDevice != NULL); 33369 MA_ASSERT(format != ma_format_unknown); 33370 MA_ASSERT(channels > 0); 33371 33372 /* Only resize the buffer if necessary. */ 33373 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { 33374 AudioBufferList* pNewAudioBufferList; 33375 33376 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); 33377 if (pNewAudioBufferList == NULL) { 33378 return MA_OUT_OF_MEMORY; 33379 } 33380 33381 /* At this point we'll have a new AudioBufferList and we can free the old one. */ 33382 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); 33383 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; 33384 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; 33385 } 33386 33387 /* Getting here means the capacity of the audio is fine. */ 33388 return MA_SUCCESS; 33389 } 33390 33391 33392 static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) 33393 { 33394 ma_device* pDevice = (ma_device*)pUserData; 33395 ma_stream_layout layout; 33396 33397 MA_ASSERT(pDevice != NULL); 33398 33399 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ 33400 33401 /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ 33402 layout = ma_stream_layout_interleaved; 33403 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { 33404 layout = ma_stream_layout_deinterleaved; 33405 } 33406 33407 if (layout == ma_stream_layout_interleaved) { 33408 /* For now we can assume everything is interleaved. */ 33409 UInt32 iBuffer; 33410 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { 33411 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { 33412 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 33413 if (frameCountForThisBuffer > 0) { 33414 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); 33415 } 33416 33417 /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ 33418 } else { 33419 /* 33420 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's 33421 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just 33422 output silence here. 33423 */ 33424 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); 33425 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ 33426 } 33427 } 33428 } else { 33429 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ 33430 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ 33431 33432 /* 33433 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something 33434 very strange has happened and we're not going to support it. 33435 */ 33436 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { 33437 ma_uint8 tempBuffer[4096]; 33438 UInt32 iBuffer; 33439 33440 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { 33441 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); 33442 ma_uint32 framesRemaining = frameCountPerBuffer; 33443 33444 while (framesRemaining > 0) { 33445 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; 33446 ma_uint32 iChannel; 33447 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 33448 if (framesToRead > framesRemaining) { 33449 framesToRead = framesRemaining; 33450 } 33451 33452 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); 33453 33454 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { 33455 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); 33456 } 33457 33458 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); 33459 33460 framesRemaining -= framesToRead; 33461 } 33462 } 33463 } 33464 } 33465 33466 (void)pActionFlags; 33467 (void)pTimeStamp; 33468 (void)busNumber; 33469 (void)frameCount; 33470 33471 return noErr; 33472 } 33473 33474 static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) 33475 { 33476 ma_device* pDevice = (ma_device*)pUserData; 33477 AudioBufferList* pRenderedBufferList; 33478 ma_result result; 33479 ma_stream_layout layout; 33480 ma_uint32 iBuffer; 33481 OSStatus status; 33482 33483 MA_ASSERT(pDevice != NULL); 33484 33485 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; 33486 MA_ASSERT(pRenderedBufferList); 33487 33488 /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ 33489 layout = ma_stream_layout_interleaved; 33490 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { 33491 layout = ma_stream_layout_deinterleaved; 33492 } 33493 33494 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ 33495 33496 /* 33497 There has been a situation reported where frame count passed into this function is greater than the capacity of 33498 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, 33499 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the 33500 number of frames requested by this callback. 33501 */ 33502 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); 33503 if (result != MA_SUCCESS) { 33504 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); 33505 return noErr; 33506 } 33507 33508 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; 33509 MA_ASSERT(pRenderedBufferList); 33510 33511 /* 33512 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes 33513 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer 33514 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a 33515 problem when a future call to this callback specifies a larger number of frames. 33516 33517 To work around this we need to explicitly set the size of each buffer to their respective size in bytes. 33518 */ 33519 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { 33520 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; 33521 } 33522 33523 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); 33524 if (status != noErr) { 33525 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); 33526 return status; 33527 } 33528 33529 if (layout == ma_stream_layout_interleaved) { 33530 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { 33531 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { 33532 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); 33533 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ 33534 } else { 33535 /* 33536 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's 33537 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. 33538 */ 33539 ma_uint8 silentBuffer[4096]; 33540 ma_uint32 framesRemaining; 33541 33542 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); 33543 33544 framesRemaining = frameCount; 33545 while (framesRemaining > 0) { 33546 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 33547 if (framesToSend > framesRemaining) { 33548 framesToSend = framesRemaining; 33549 } 33550 33551 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); 33552 33553 framesRemaining -= framesToSend; 33554 } 33555 33556 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ 33557 } 33558 } 33559 } else { 33560 /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ 33561 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ 33562 33563 /* 33564 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something 33565 very strange has happened and we're not going to support it. 33566 */ 33567 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { 33568 ma_uint8 tempBuffer[4096]; 33569 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { 33570 ma_uint32 framesRemaining = frameCount; 33571 while (framesRemaining > 0) { 33572 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; 33573 ma_uint32 iChannel; 33574 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 33575 if (framesToSend > framesRemaining) { 33576 framesToSend = framesRemaining; 33577 } 33578 33579 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { 33580 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); 33581 } 33582 33583 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); 33584 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); 33585 33586 framesRemaining -= framesToSend; 33587 } 33588 } 33589 } 33590 } 33591 33592 (void)pActionFlags; 33593 (void)pTimeStamp; 33594 (void)busNumber; 33595 (void)frameCount; 33596 (void)pUnusedBufferList; 33597 33598 return noErr; 33599 } 33600 33601 static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) 33602 { 33603 ma_device* pDevice = (ma_device*)pUserData; 33604 MA_ASSERT(pDevice != NULL); 33605 33606 /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ 33607 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || 33608 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { 33609 return; 33610 } 33611 33612 /* 33613 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like 33614 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) 33615 can try waiting on the same lock. I'm going to try working around this by not calling any Core 33616 Audio APIs in the callback when the device has been stopped or uninitialized. 33617 */ 33618 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { 33619 ma_device__on_notification_stopped(pDevice); 33620 } else { 33621 UInt32 isRunning; 33622 UInt32 isRunningSize = sizeof(isRunning); 33623 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); 33624 if (status != noErr) { 33625 goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ 33626 } 33627 33628 if (!isRunning) { 33629 /* 33630 The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: 33631 33632 1) When the device is unplugged, this will be called _before_ the default device change notification. 33633 2) When the device is changed via the default device change notification, this will be called _after_ the switch. 33634 33635 For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. 33636 */ 33637 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || 33638 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { 33639 /* 33640 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device 33641 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the 33642 device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it 33643 hasn't!). 33644 */ 33645 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || 33646 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { 33647 goto done; 33648 } 33649 33650 /* 33651 Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio 33652 will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most 33653 likely be successful in switching to the new device. 33654 33655 TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. 33656 */ 33657 goto done; 33658 } 33659 33660 /* Getting here means we need to stop the device. */ 33661 ma_device__on_notification_stopped(pDevice); 33662 } 33663 } 33664 33665 (void)propertyID; /* Unused. */ 33666 33667 done: 33668 /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ 33669 ma_event_signal(&pDevice->coreaudio.stopEvent); 33670 } 33671 33672 #if defined(MA_APPLE_DESKTOP) 33673 static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ 33674 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; 33675 static ma_mutex g_DeviceTrackingMutex_CoreAudio; 33676 static ma_device** g_ppTrackedDevices_CoreAudio = NULL; 33677 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; 33678 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; 33679 33680 static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) 33681 { 33682 ma_device_type deviceType; 33683 33684 /* Not sure if I really need to check this, but it makes me feel better. */ 33685 if (addressCount == 0) { 33686 return noErr; 33687 } 33688 33689 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { 33690 deviceType = ma_device_type_playback; 33691 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { 33692 deviceType = ma_device_type_capture; 33693 } else { 33694 return noErr; /* Should never hit this. */ 33695 } 33696 33697 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); 33698 { 33699 ma_uint32 iDevice; 33700 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { 33701 ma_result reinitResult; 33702 ma_device* pDevice; 33703 33704 pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; 33705 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { 33706 if (deviceType == ma_device_type_playback) { 33707 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; 33708 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); 33709 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; 33710 } else { 33711 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; 33712 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); 33713 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; 33714 } 33715 33716 if (reinitResult == MA_SUCCESS) { 33717 ma_device__post_init_setup(pDevice, deviceType); 33718 33719 /* Restart the device if required. If this fails we need to stop the device entirely. */ 33720 if (ma_device_get_state(pDevice) == ma_device_state_started) { 33721 OSStatus status; 33722 if (deviceType == ma_device_type_playback) { 33723 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 33724 if (status != noErr) { 33725 if (pDevice->type == ma_device_type_duplex) { 33726 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 33727 } 33728 ma_device__set_state(pDevice, ma_device_state_stopped); 33729 } 33730 } else if (deviceType == ma_device_type_capture) { 33731 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 33732 if (status != noErr) { 33733 if (pDevice->type == ma_device_type_duplex) { 33734 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 33735 } 33736 ma_device__set_state(pDevice, ma_device_state_stopped); 33737 } 33738 } 33739 } 33740 33741 ma_device__on_notification_rerouted(pDevice); 33742 } 33743 } 33744 } 33745 } 33746 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); 33747 33748 /* Unused parameters. */ 33749 (void)objectID; 33750 (void)pUserData; 33751 33752 return noErr; 33753 } 33754 33755 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) 33756 { 33757 MA_ASSERT(pContext != NULL); 33758 33759 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); 33760 { 33761 /* Don't do anything if we've already initializd device tracking. */ 33762 if (g_DeviceTrackingInitCounter_CoreAudio == 0) { 33763 AudioObjectPropertyAddress propAddress; 33764 propAddress.mScope = kAudioObjectPropertyScopeGlobal; 33765 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 33766 33767 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); 33768 33769 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; 33770 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); 33771 33772 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; 33773 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); 33774 33775 } 33776 g_DeviceTrackingInitCounter_CoreAudio += 1; 33777 } 33778 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); 33779 33780 return MA_SUCCESS; 33781 } 33782 33783 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) 33784 { 33785 MA_ASSERT(pContext != NULL); 33786 33787 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); 33788 { 33789 if (g_DeviceTrackingInitCounter_CoreAudio > 0) 33790 g_DeviceTrackingInitCounter_CoreAudio -= 1; 33791 33792 if (g_DeviceTrackingInitCounter_CoreAudio == 0) { 33793 AudioObjectPropertyAddress propAddress; 33794 propAddress.mScope = kAudioObjectPropertyScopeGlobal; 33795 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 33796 33797 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; 33798 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); 33799 33800 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; 33801 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); 33802 33803 /* At this point there should be no tracked devices. If not there's an error somewhere. */ 33804 if (g_ppTrackedDevices_CoreAudio != NULL) { 33805 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); 33806 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); 33807 return MA_INVALID_OPERATION; 33808 } 33809 33810 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); 33811 } 33812 } 33813 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); 33814 33815 return MA_SUCCESS; 33816 } 33817 33818 static ma_result ma_device__track__coreaudio(ma_device* pDevice) 33819 { 33820 MA_ASSERT(pDevice != NULL); 33821 33822 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); 33823 { 33824 /* Allocate memory if required. */ 33825 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { 33826 ma_uint32 newCap; 33827 ma_device** ppNewDevices; 33828 33829 newCap = g_TrackedDeviceCap_CoreAudio * 2; 33830 if (newCap == 0) { 33831 newCap = 1; 33832 } 33833 33834 ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); 33835 if (ppNewDevices == NULL) { 33836 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); 33837 return MA_OUT_OF_MEMORY; 33838 } 33839 33840 g_ppTrackedDevices_CoreAudio = ppNewDevices; 33841 g_TrackedDeviceCap_CoreAudio = newCap; 33842 } 33843 33844 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; 33845 g_TrackedDeviceCount_CoreAudio += 1; 33846 } 33847 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); 33848 33849 return MA_SUCCESS; 33850 } 33851 33852 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) 33853 { 33854 MA_ASSERT(pDevice != NULL); 33855 33856 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); 33857 { 33858 ma_uint32 iDevice; 33859 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { 33860 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { 33861 /* We've found the device. We now need to remove it from the list. */ 33862 ma_uint32 jDevice; 33863 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { 33864 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; 33865 } 33866 33867 g_TrackedDeviceCount_CoreAudio -= 1; 33868 33869 /* If there's nothing else in the list we need to free memory. */ 33870 if (g_TrackedDeviceCount_CoreAudio == 0) { 33871 ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); 33872 g_ppTrackedDevices_CoreAudio = NULL; 33873 g_TrackedDeviceCap_CoreAudio = 0; 33874 } 33875 33876 break; 33877 } 33878 } 33879 } 33880 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); 33881 33882 return MA_SUCCESS; 33883 } 33884 #endif 33885 33886 #if defined(MA_APPLE_MOBILE) 33887 @interface ma_ios_notification_handler:NSObject { 33888 ma_device* m_pDevice; 33889 } 33890 @end 33891 33892 @implementation ma_ios_notification_handler 33893 -(id)init:(ma_device*)pDevice 33894 { 33895 self = [super init]; 33896 m_pDevice = pDevice; 33897 33898 /* For route changes. */ 33899 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; 33900 33901 /* For interruptions. */ 33902 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; 33903 33904 return self; 33905 } 33906 33907 -(void)dealloc 33908 { 33909 [self remove_handler]; 33910 33911 #if defined(__has_feature) 33912 #if !__has_feature(objc_arc) 33913 [super dealloc]; 33914 #endif 33915 #endif 33916 } 33917 33918 -(void)remove_handler 33919 { 33920 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; 33921 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; 33922 } 33923 33924 -(void)handle_interruption:(NSNotification*)pNotification 33925 { 33926 NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; 33927 switch (type) 33928 { 33929 case AVAudioSessionInterruptionTypeBegan: 33930 { 33931 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); 33932 33933 /* 33934 Core Audio will have stopped the internal device automatically, but we need explicitly 33935 stop it at a higher level to ensure miniaudio-specific state is updated for consistency. 33936 */ 33937 ma_device_stop(m_pDevice); 33938 33939 /* 33940 Fire the notification after the device has been stopped to ensure it's in the correct 33941 state when the notification handler is invoked. 33942 */ 33943 ma_device__on_notification_interruption_began(m_pDevice); 33944 } break; 33945 33946 case AVAudioSessionInterruptionTypeEnded: 33947 { 33948 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); 33949 ma_device__on_notification_interruption_ended(m_pDevice); 33950 } break; 33951 } 33952 } 33953 33954 -(void)handle_route_change:(NSNotification*)pNotification 33955 { 33956 AVAudioSession* pSession = [AVAudioSession sharedInstance]; 33957 33958 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; 33959 switch (reason) 33960 { 33961 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: 33962 { 33963 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); 33964 } break; 33965 33966 case AVAudioSessionRouteChangeReasonNewDeviceAvailable: 33967 { 33968 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); 33969 } break; 33970 33971 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: 33972 { 33973 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); 33974 } break; 33975 33976 case AVAudioSessionRouteChangeReasonWakeFromSleep: 33977 { 33978 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); 33979 } break; 33980 33981 case AVAudioSessionRouteChangeReasonOverride: 33982 { 33983 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); 33984 } break; 33985 33986 case AVAudioSessionRouteChangeReasonCategoryChange: 33987 { 33988 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); 33989 } break; 33990 33991 case AVAudioSessionRouteChangeReasonUnknown: 33992 default: 33993 { 33994 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); 33995 } break; 33996 } 33997 33998 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); 33999 34000 /* Let the application know about the route change. */ 34001 ma_device__on_notification_rerouted(m_pDevice); 34002 } 34003 @end 34004 #endif 34005 34006 static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) 34007 { 34008 MA_ASSERT(pDevice != NULL); 34009 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); 34010 34011 #if defined(MA_APPLE_DESKTOP) 34012 /* 34013 Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll 34014 just gracefully ignore it. 34015 */ 34016 ma_device__untrack__coreaudio(pDevice); 34017 #endif 34018 #if defined(MA_APPLE_MOBILE) 34019 if (pDevice->coreaudio.pNotificationHandler != NULL) { 34020 ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; 34021 [pNotificationHandler remove_handler]; 34022 } 34023 #endif 34024 34025 if (pDevice->coreaudio.audioUnitCapture != NULL) { 34026 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34027 } 34028 if (pDevice->coreaudio.audioUnitPlayback != NULL) { 34029 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 34030 } 34031 34032 if (pDevice->coreaudio.pAudioBufferList) { 34033 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); 34034 } 34035 34036 return MA_SUCCESS; 34037 } 34038 34039 typedef struct 34040 { 34041 ma_bool32 allowNominalSampleRateChange; 34042 34043 /* Input. */ 34044 ma_format formatIn; 34045 ma_uint32 channelsIn; 34046 ma_uint32 sampleRateIn; 34047 ma_channel channelMapIn[MA_MAX_CHANNELS]; 34048 ma_uint32 periodSizeInFramesIn; 34049 ma_uint32 periodSizeInMillisecondsIn; 34050 ma_uint32 periodsIn; 34051 ma_share_mode shareMode; 34052 ma_performance_profile performanceProfile; 34053 ma_bool32 registerStopEvent; 34054 34055 /* Output. */ 34056 #if defined(MA_APPLE_DESKTOP) 34057 AudioObjectID deviceObjectID; 34058 #endif 34059 AudioComponent component; 34060 AudioUnit audioUnit; 34061 AudioBufferList* pAudioBufferList; /* Only used for input devices. */ 34062 ma_format formatOut; 34063 ma_uint32 channelsOut; 34064 ma_uint32 sampleRateOut; 34065 ma_channel channelMapOut[MA_MAX_CHANNELS]; 34066 ma_uint32 periodSizeInFramesOut; 34067 ma_uint32 periodsOut; 34068 char deviceName[256]; 34069 } ma_device_init_internal_data__coreaudio; 34070 34071 static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ 34072 { 34073 ma_result result; 34074 OSStatus status; 34075 UInt32 enableIOFlag; 34076 AudioStreamBasicDescription bestFormat; 34077 UInt32 actualPeriodSizeInFrames; 34078 AURenderCallbackStruct callbackInfo; 34079 #if defined(MA_APPLE_DESKTOP) 34080 AudioObjectID deviceObjectID; 34081 #endif 34082 34083 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ 34084 if (deviceType == ma_device_type_duplex) { 34085 return MA_INVALID_ARGS; 34086 } 34087 34088 MA_ASSERT(pContext != NULL); 34089 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); 34090 34091 #if defined(MA_APPLE_DESKTOP) 34092 pData->deviceObjectID = 0; 34093 #endif 34094 pData->component = NULL; 34095 pData->audioUnit = NULL; 34096 pData->pAudioBufferList = NULL; 34097 34098 #if defined(MA_APPLE_DESKTOP) 34099 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); 34100 if (result != MA_SUCCESS) { 34101 return result; 34102 } 34103 34104 pData->deviceObjectID = deviceObjectID; 34105 #endif 34106 34107 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ 34108 pData->periodsOut = pData->periodsIn; 34109 if (pData->periodsOut == 0) { 34110 pData->periodsOut = MA_DEFAULT_PERIODS; 34111 } 34112 if (pData->periodsOut > 16) { 34113 pData->periodsOut = 16; 34114 } 34115 34116 34117 /* Audio unit. */ 34118 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); 34119 if (status != noErr) { 34120 return ma_result_from_OSStatus(status); 34121 } 34122 34123 34124 /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ 34125 enableIOFlag = 1; 34126 if (deviceType == ma_device_type_capture) { 34127 enableIOFlag = 0; 34128 } 34129 34130 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); 34131 if (status != noErr) { 34132 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34133 return ma_result_from_OSStatus(status); 34134 } 34135 34136 enableIOFlag = (enableIOFlag == 0) ? 1 : 0; 34137 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); 34138 if (status != noErr) { 34139 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34140 return ma_result_from_OSStatus(status); 34141 } 34142 34143 34144 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ 34145 #if defined(MA_APPLE_DESKTOP) 34146 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); 34147 if (status != noErr) { 34148 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34149 return ma_result_from_OSStatus(result); 34150 } 34151 #else 34152 /* 34153 For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change 34154 the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. 34155 */ 34156 if (pDeviceID != NULL) { 34157 if (deviceType == ma_device_type_capture) { 34158 ma_bool32 found = MA_FALSE; 34159 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; 34160 for (AVAudioSessionPortDescription* pPortDesc in pInputs) { 34161 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { 34162 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; 34163 found = MA_TRUE; 34164 break; 34165 } 34166 } 34167 34168 if (found == MA_FALSE) { 34169 return MA_DOES_NOT_EXIST; 34170 } 34171 } 34172 } 34173 #endif 34174 34175 /* 34176 Format. This is the hardest part of initialization because there's a few variables to take into account. 34177 1) The format must be supported by the device. 34178 2) The format must be supported miniaudio. 34179 3) There's a priority that miniaudio prefers. 34180 34181 Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The 34182 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same 34183 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. 34184 34185 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. 34186 */ 34187 { 34188 AudioStreamBasicDescription origFormat; 34189 UInt32 origFormatSize = sizeof(origFormat); 34190 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; 34191 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; 34192 34193 if (deviceType == ma_device_type_playback) { 34194 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); 34195 } else { 34196 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); 34197 } 34198 if (status != noErr) { 34199 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34200 return ma_result_from_OSStatus(status); 34201 } 34202 34203 #if defined(MA_APPLE_DESKTOP) 34204 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); 34205 if (result != MA_SUCCESS) { 34206 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34207 return result; 34208 } 34209 34210 /* 34211 Technical Note TN2091: Device input using the HAL Output Audio Unit 34212 https://developer.apple.com/library/archive/technotes/tn2091/_index.html 34213 34214 This documentation says the following: 34215 34216 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY 34217 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate 34218 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with 34219 another AudioConverter. 34220 34221 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We 34222 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it 34223 safe and apply the same rule to output as well. 34224 34225 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() 34226 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but 34227 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. 34228 34229 Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with 34230 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This 34231 could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a 34232 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample 34233 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run 34234 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is 34235 changed by miniaudio. 34236 */ 34237 if (pData->allowNominalSampleRateChange) { 34238 AudioValueRange sampleRateRange; 34239 AudioObjectPropertyAddress propAddress; 34240 34241 sampleRateRange.mMinimum = bestFormat.mSampleRate; 34242 sampleRateRange.mMaximum = bestFormat.mSampleRate; 34243 34244 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; 34245 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; 34246 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; 34247 34248 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); 34249 if (status != noErr) { 34250 bestFormat.mSampleRate = origFormat.mSampleRate; 34251 } 34252 } else { 34253 bestFormat.mSampleRate = origFormat.mSampleRate; 34254 } 34255 34256 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); 34257 if (status != noErr) { 34258 /* We failed to set the format, so fall back to the current format of the audio unit. */ 34259 bestFormat = origFormat; 34260 } 34261 #else 34262 bestFormat = origFormat; 34263 34264 /* 34265 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try 34266 setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since 34267 it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I 34268 can tell, it looks like the sample rate is shared between playback and capture for everything. 34269 */ 34270 @autoreleasepool { 34271 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; 34272 MA_ASSERT(pAudioSession != NULL); 34273 34274 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; 34275 bestFormat.mSampleRate = pAudioSession.sampleRate; 34276 34277 /* 34278 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with 34279 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. 34280 */ 34281 if (deviceType == ma_device_type_playback) { 34282 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; 34283 } 34284 if (deviceType == ma_device_type_capture) { 34285 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; 34286 } 34287 } 34288 34289 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); 34290 if (status != noErr) { 34291 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34292 return ma_result_from_OSStatus(status); 34293 } 34294 #endif 34295 34296 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); 34297 if (result != MA_SUCCESS) { 34298 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34299 return result; 34300 } 34301 34302 if (pData->formatOut == ma_format_unknown) { 34303 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34304 return MA_FORMAT_NOT_SUPPORTED; 34305 } 34306 34307 pData->channelsOut = bestFormat.mChannelsPerFrame; 34308 pData->sampleRateOut = bestFormat.mSampleRate; 34309 } 34310 34311 /* Clamp the channel count for safety. */ 34312 if (pData->channelsOut > MA_MAX_CHANNELS) { 34313 pData->channelsOut = MA_MAX_CHANNELS; 34314 } 34315 34316 /* 34317 Internal channel map. This is weird in my testing. If I use the AudioObject to get the 34318 channel map, the channel descriptions are set to "Unknown" for some reason. To work around 34319 this it looks like retrieving it from the AudioUnit will work. However, and this is where 34320 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore 34321 I'm going to fall back to a default assumption in these cases. 34322 */ 34323 #if defined(MA_APPLE_DESKTOP) 34324 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); 34325 if (result != MA_SUCCESS) { 34326 #if 0 34327 /* Try falling back to the channel map from the AudioObject. */ 34328 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); 34329 if (result != MA_SUCCESS) { 34330 return result; 34331 } 34332 #else 34333 /* Fall back to default assumptions. */ 34334 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); 34335 #endif 34336 } 34337 #else 34338 /* TODO: Figure out how to get the channel map using AVAudioSession. */ 34339 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); 34340 #endif 34341 34342 34343 /* Buffer size. Not allowing this to be configurable on iOS. */ 34344 if (pData->periodSizeInFramesIn == 0) { 34345 if (pData->periodSizeInMillisecondsIn == 0) { 34346 if (pData->performanceProfile == ma_performance_profile_low_latency) { 34347 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); 34348 } else { 34349 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); 34350 } 34351 } else { 34352 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); 34353 } 34354 } else { 34355 actualPeriodSizeInFrames = pData->periodSizeInFramesIn; 34356 } 34357 34358 #if defined(MA_APPLE_DESKTOP) 34359 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); 34360 if (result != MA_SUCCESS) { 34361 return result; 34362 } 34363 #else 34364 /* 34365 On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point 34366 number. I don't trust any potential truncation errors due to converting from float to integer 34367 so I'm going to explicitly set the actual period size to the next power of 2. 34368 */ 34369 @autoreleasepool { 34370 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; 34371 MA_ASSERT(pAudioSession != NULL); 34372 34373 [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; 34374 actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); 34375 } 34376 #endif 34377 34378 34379 /* 34380 During testing I discovered that the buffer size can be too big. You'll get an error like this: 34381 34382 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 34383 34384 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that 34385 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. 34386 */ 34387 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); 34388 if (status != noErr) { 34389 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34390 return ma_result_from_OSStatus(status); 34391 } 34392 34393 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; 34394 34395 /* We need a buffer list if this is an input device. We render into this in the input callback. */ 34396 if (deviceType == ma_device_type_capture) { 34397 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; 34398 AudioBufferList* pBufferList; 34399 34400 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); 34401 if (pBufferList == NULL) { 34402 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34403 return MA_OUT_OF_MEMORY; 34404 } 34405 34406 pData->pAudioBufferList = pBufferList; 34407 } 34408 34409 /* Callbacks. */ 34410 callbackInfo.inputProcRefCon = pDevice_DoNotReference; 34411 if (deviceType == ma_device_type_playback) { 34412 callbackInfo.inputProc = ma_on_output__coreaudio; 34413 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); 34414 if (status != noErr) { 34415 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34416 return ma_result_from_OSStatus(status); 34417 } 34418 } else { 34419 callbackInfo.inputProc = ma_on_input__coreaudio; 34420 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); 34421 if (status != noErr) { 34422 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34423 return ma_result_from_OSStatus(status); 34424 } 34425 } 34426 34427 /* We need to listen for stop events. */ 34428 if (pData->registerStopEvent) { 34429 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); 34430 if (status != noErr) { 34431 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34432 return ma_result_from_OSStatus(status); 34433 } 34434 } 34435 34436 /* Initialize the audio unit. */ 34437 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); 34438 if (status != noErr) { 34439 ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); 34440 pData->pAudioBufferList = NULL; 34441 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); 34442 return ma_result_from_OSStatus(status); 34443 } 34444 34445 /* Grab the name. */ 34446 #if defined(MA_APPLE_DESKTOP) 34447 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); 34448 #else 34449 if (deviceType == ma_device_type_playback) { 34450 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); 34451 } else { 34452 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); 34453 } 34454 #endif 34455 34456 return result; 34457 } 34458 34459 #if defined(MA_APPLE_DESKTOP) 34460 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) 34461 { 34462 ma_device_init_internal_data__coreaudio data; 34463 ma_result result; 34464 34465 /* This should only be called for playback or capture, not duplex. */ 34466 if (deviceType == ma_device_type_duplex) { 34467 return MA_INVALID_ARGS; 34468 } 34469 34470 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ 34471 34472 if (deviceType == ma_device_type_capture) { 34473 data.formatIn = pDevice->capture.format; 34474 data.channelsIn = pDevice->capture.channels; 34475 data.sampleRateIn = pDevice->sampleRate; 34476 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); 34477 data.shareMode = pDevice->capture.shareMode; 34478 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; 34479 data.registerStopEvent = MA_TRUE; 34480 34481 if (disposePreviousAudioUnit) { 34482 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34483 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34484 } 34485 if (pDevice->coreaudio.pAudioBufferList) { 34486 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); 34487 } 34488 } else if (deviceType == ma_device_type_playback) { 34489 data.formatIn = pDevice->playback.format; 34490 data.channelsIn = pDevice->playback.channels; 34491 data.sampleRateIn = pDevice->sampleRate; 34492 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); 34493 data.shareMode = pDevice->playback.shareMode; 34494 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; 34495 data.registerStopEvent = (pDevice->type != ma_device_type_duplex); 34496 34497 if (disposePreviousAudioUnit) { 34498 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 34499 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 34500 } 34501 } 34502 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; 34503 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; 34504 data.periodsIn = pDevice->coreaudio.originalPeriods; 34505 34506 /* Need at least 3 periods for duplex. */ 34507 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { 34508 data.periodsIn = 3; 34509 } 34510 34511 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); 34512 if (result != MA_SUCCESS) { 34513 return result; 34514 } 34515 34516 if (deviceType == ma_device_type_capture) { 34517 #if defined(MA_APPLE_DESKTOP) 34518 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; 34519 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); 34520 #endif 34521 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; 34522 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; 34523 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; 34524 34525 pDevice->capture.internalFormat = data.formatOut; 34526 pDevice->capture.internalChannels = data.channelsOut; 34527 pDevice->capture.internalSampleRate = data.sampleRateOut; 34528 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); 34529 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; 34530 pDevice->capture.internalPeriods = data.periodsOut; 34531 } else if (deviceType == ma_device_type_playback) { 34532 #if defined(MA_APPLE_DESKTOP) 34533 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; 34534 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); 34535 #endif 34536 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; 34537 34538 pDevice->playback.internalFormat = data.formatOut; 34539 pDevice->playback.internalChannels = data.channelsOut; 34540 pDevice->playback.internalSampleRate = data.sampleRateOut; 34541 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); 34542 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; 34543 pDevice->playback.internalPeriods = data.periodsOut; 34544 } 34545 34546 return MA_SUCCESS; 34547 } 34548 #endif /* MA_APPLE_DESKTOP */ 34549 34550 static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 34551 { 34552 ma_result result; 34553 34554 MA_ASSERT(pDevice != NULL); 34555 MA_ASSERT(pConfig != NULL); 34556 34557 if (pConfig->deviceType == ma_device_type_loopback) { 34558 return MA_DEVICE_TYPE_NOT_SUPPORTED; 34559 } 34560 34561 /* No exclusive mode with the Core Audio backend for now. */ 34562 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || 34563 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { 34564 return MA_SHARE_MODE_NOT_SUPPORTED; 34565 } 34566 34567 /* Capture needs to be initialized first. */ 34568 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 34569 ma_device_init_internal_data__coreaudio data; 34570 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; 34571 data.formatIn = pDescriptorCapture->format; 34572 data.channelsIn = pDescriptorCapture->channels; 34573 data.sampleRateIn = pDescriptorCapture->sampleRate; 34574 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); 34575 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; 34576 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; 34577 data.periodsIn = pDescriptorCapture->periodCount; 34578 data.shareMode = pDescriptorCapture->shareMode; 34579 data.performanceProfile = pConfig->performanceProfile; 34580 data.registerStopEvent = MA_TRUE; 34581 34582 /* Need at least 3 periods for duplex. */ 34583 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { 34584 data.periodsIn = 3; 34585 } 34586 34587 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); 34588 if (result != MA_SUCCESS) { 34589 return result; 34590 } 34591 34592 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); 34593 #if defined(MA_APPLE_DESKTOP) 34594 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; 34595 #endif 34596 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; 34597 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; 34598 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; 34599 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; 34600 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; 34601 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; 34602 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; 34603 34604 pDescriptorCapture->format = data.formatOut; 34605 pDescriptorCapture->channels = data.channelsOut; 34606 pDescriptorCapture->sampleRate = data.sampleRateOut; 34607 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); 34608 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; 34609 pDescriptorCapture->periodCount = data.periodsOut; 34610 34611 #if defined(MA_APPLE_DESKTOP) 34612 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); 34613 34614 /* 34615 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly 34616 switch the device in the background. 34617 */ 34618 if (pConfig->capture.pDeviceID == NULL) { 34619 ma_device__track__coreaudio(pDevice); 34620 } 34621 #endif 34622 } 34623 34624 /* Playback. */ 34625 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 34626 ma_device_init_internal_data__coreaudio data; 34627 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; 34628 data.formatIn = pDescriptorPlayback->format; 34629 data.channelsIn = pDescriptorPlayback->channels; 34630 data.sampleRateIn = pDescriptorPlayback->sampleRate; 34631 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); 34632 data.shareMode = pDescriptorPlayback->shareMode; 34633 data.performanceProfile = pConfig->performanceProfile; 34634 34635 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ 34636 if (pConfig->deviceType == ma_device_type_duplex) { 34637 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; 34638 data.periodsIn = pDescriptorCapture->periodCount; 34639 data.registerStopEvent = MA_FALSE; 34640 } else { 34641 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; 34642 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; 34643 data.periodsIn = pDescriptorPlayback->periodCount; 34644 data.registerStopEvent = MA_TRUE; 34645 } 34646 34647 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); 34648 if (result != MA_SUCCESS) { 34649 if (pConfig->deviceType == ma_device_type_duplex) { 34650 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34651 if (pDevice->coreaudio.pAudioBufferList) { 34652 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); 34653 } 34654 } 34655 return result; 34656 } 34657 34658 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); 34659 #if defined(MA_APPLE_DESKTOP) 34660 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; 34661 #endif 34662 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; 34663 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; 34664 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; 34665 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; 34666 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; 34667 34668 pDescriptorPlayback->format = data.formatOut; 34669 pDescriptorPlayback->channels = data.channelsOut; 34670 pDescriptorPlayback->sampleRate = data.sampleRateOut; 34671 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); 34672 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; 34673 pDescriptorPlayback->periodCount = data.periodsOut; 34674 34675 #if defined(MA_APPLE_DESKTOP) 34676 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); 34677 34678 /* 34679 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly 34680 switch the device in the background. 34681 */ 34682 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { 34683 ma_device__track__coreaudio(pDevice); 34684 } 34685 #endif 34686 } 34687 34688 34689 34690 /* 34691 When stopping the device, a callback is called on another thread. We need to wait for this callback 34692 before returning from ma_device_stop(). This event is used for this. 34693 */ 34694 ma_event_init(&pDevice->coreaudio.stopEvent); 34695 34696 /* 34697 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done 34698 differently on non-Desktop Apple platforms. 34699 */ 34700 #if defined(MA_APPLE_MOBILE) 34701 pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; 34702 #endif 34703 34704 return MA_SUCCESS; 34705 } 34706 34707 34708 static ma_result ma_device_start__coreaudio(ma_device* pDevice) 34709 { 34710 MA_ASSERT(pDevice != NULL); 34711 34712 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 34713 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34714 if (status != noErr) { 34715 return ma_result_from_OSStatus(status); 34716 } 34717 } 34718 34719 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 34720 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 34721 if (status != noErr) { 34722 if (pDevice->type == ma_device_type_duplex) { 34723 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34724 } 34725 return ma_result_from_OSStatus(status); 34726 } 34727 } 34728 34729 return MA_SUCCESS; 34730 } 34731 34732 static ma_result ma_device_stop__coreaudio(ma_device* pDevice) 34733 { 34734 MA_ASSERT(pDevice != NULL); 34735 34736 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ 34737 34738 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 34739 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); 34740 if (status != noErr) { 34741 return ma_result_from_OSStatus(status); 34742 } 34743 } 34744 34745 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 34746 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); 34747 if (status != noErr) { 34748 return ma_result_from_OSStatus(status); 34749 } 34750 } 34751 34752 /* We need to wait for the callback to finish before returning. */ 34753 ma_event_wait(&pDevice->coreaudio.stopEvent); 34754 return MA_SUCCESS; 34755 } 34756 34757 34758 static ma_result ma_context_uninit__coreaudio(ma_context* pContext) 34759 { 34760 MA_ASSERT(pContext != NULL); 34761 MA_ASSERT(pContext->backend == ma_backend_coreaudio); 34762 34763 #if defined(MA_APPLE_MOBILE) 34764 if (!pContext->coreaudio.noAudioSessionDeactivate) { 34765 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { 34766 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); 34767 return MA_FAILED_TO_INIT_BACKEND; 34768 } 34769 } 34770 #endif 34771 34772 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) 34773 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); 34774 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); 34775 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34776 #endif 34777 34778 #if !defined(MA_APPLE_MOBILE) 34779 ma_context__uninit_device_tracking__coreaudio(pContext); 34780 #endif 34781 34782 (void)pContext; 34783 return MA_SUCCESS; 34784 } 34785 34786 #if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) 34787 static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) 34788 { 34789 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ 34790 MA_ASSERT(category != ma_ios_session_category_default); 34791 MA_ASSERT(category != ma_ios_session_category_none); 34792 34793 switch (category) { 34794 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; 34795 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; 34796 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; 34797 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; 34798 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; 34799 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; 34800 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; 34801 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; 34802 default: return AVAudioSessionCategoryAmbient; 34803 } 34804 } 34805 #endif 34806 34807 static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 34808 { 34809 #if !defined(MA_APPLE_MOBILE) 34810 ma_result result; 34811 #endif 34812 34813 MA_ASSERT(pConfig != NULL); 34814 MA_ASSERT(pContext != NULL); 34815 34816 #if defined(MA_APPLE_MOBILE) 34817 @autoreleasepool { 34818 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; 34819 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; 34820 34821 MA_ASSERT(pAudioSession != NULL); 34822 34823 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { 34824 /* 34825 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails 34826 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. 34827 */ 34828 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) 34829 options |= AVAudioSessionCategoryOptionDefaultToSpeaker; 34830 #endif 34831 34832 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { 34833 /* Using PlayAndRecord */ 34834 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { 34835 /* Using Playback */ 34836 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { 34837 /* Using Record */ 34838 } else { 34839 /* Leave as default? */ 34840 } 34841 } else { 34842 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { 34843 #if defined(__IPHONE_12_0) 34844 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { 34845 return MA_INVALID_OPERATION; /* Failed to set session category. */ 34846 } 34847 #else 34848 /* Ignore the session category on version 11 and older, but post a warning. */ 34849 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); 34850 #endif 34851 } 34852 } 34853 34854 if (!pConfig->coreaudio.noAudioSessionActivate) { 34855 if (![pAudioSession setActive:true error:nil]) { 34856 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); 34857 return MA_FAILED_TO_INIT_BACKEND; 34858 } 34859 } 34860 } 34861 #endif 34862 34863 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) 34864 pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); 34865 if (pContext->coreaudio.hCoreFoundation == NULL) { 34866 return MA_API_NOT_FOUND; 34867 } 34868 34869 pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); 34870 pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); 34871 34872 34873 pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); 34874 if (pContext->coreaudio.hCoreAudio == NULL) { 34875 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34876 return MA_API_NOT_FOUND; 34877 } 34878 34879 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); 34880 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); 34881 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); 34882 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); 34883 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); 34884 34885 /* 34886 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still 34887 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. 34888 The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to 34889 AudioToolbox. 34890 */ 34891 pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); 34892 if (pContext->coreaudio.hAudioUnit == NULL) { 34893 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); 34894 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34895 return MA_API_NOT_FOUND; 34896 } 34897 34898 if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { 34899 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ 34900 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); 34901 pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); 34902 if (pContext->coreaudio.hAudioUnit == NULL) { 34903 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); 34904 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34905 return MA_API_NOT_FOUND; 34906 } 34907 } 34908 34909 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); 34910 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); 34911 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); 34912 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); 34913 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); 34914 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); 34915 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); 34916 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); 34917 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); 34918 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); 34919 pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); 34920 #else 34921 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; 34922 pContext->coreaudio.CFRelease = (ma_proc)CFRelease; 34923 34924 #if defined(MA_APPLE_DESKTOP) 34925 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; 34926 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; 34927 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; 34928 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; 34929 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; 34930 #endif 34931 34932 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; 34933 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; 34934 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; 34935 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; 34936 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; 34937 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; 34938 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; 34939 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; 34940 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; 34941 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; 34942 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; 34943 #endif 34944 34945 /* Audio component. */ 34946 { 34947 AudioComponentDescription desc; 34948 desc.componentType = kAudioUnitType_Output; 34949 #if defined(MA_APPLE_DESKTOP) 34950 desc.componentSubType = kAudioUnitSubType_HALOutput; 34951 #else 34952 desc.componentSubType = kAudioUnitSubType_RemoteIO; 34953 #endif 34954 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 34955 desc.componentFlags = 0; 34956 desc.componentFlagsMask = 0; 34957 34958 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); 34959 if (pContext->coreaudio.component == NULL) { 34960 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) 34961 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); 34962 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); 34963 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34964 #endif 34965 return MA_FAILED_TO_INIT_BACKEND; 34966 } 34967 } 34968 34969 #if !defined(MA_APPLE_MOBILE) 34970 result = ma_context__init_device_tracking__coreaudio(pContext); 34971 if (result != MA_SUCCESS) { 34972 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) 34973 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); 34974 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); 34975 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); 34976 #endif 34977 return result; 34978 } 34979 #endif 34980 34981 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; 34982 34983 pCallbacks->onContextInit = ma_context_init__coreaudio; 34984 pCallbacks->onContextUninit = ma_context_uninit__coreaudio; 34985 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; 34986 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; 34987 pCallbacks->onDeviceInit = ma_device_init__coreaudio; 34988 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; 34989 pCallbacks->onDeviceStart = ma_device_start__coreaudio; 34990 pCallbacks->onDeviceStop = ma_device_stop__coreaudio; 34991 pCallbacks->onDeviceRead = NULL; 34992 pCallbacks->onDeviceWrite = NULL; 34993 pCallbacks->onDeviceDataLoop = NULL; 34994 34995 return MA_SUCCESS; 34996 } 34997 #endif /* Core Audio */ 34998 34999 35000 35001 /****************************************************************************** 35002 35003 sndio Backend 35004 35005 ******************************************************************************/ 35006 #ifdef MA_HAS_SNDIO 35007 #include <fcntl.h> 35008 35009 /* 35010 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due 35011 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device 35012 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's 35013 demand for it or if I can get it tested and debugged more thoroughly. 35014 */ 35015 #if 0 35016 #if defined(__NetBSD__) || defined(__OpenBSD__) 35017 #include <sys/audioio.h> 35018 #endif 35019 #if defined(__FreeBSD__) || defined(__DragonFly__) 35020 #include <sys/soundcard.h> 35021 #endif 35022 #endif 35023 35024 #define MA_SIO_DEVANY "default" 35025 #define MA_SIO_PLAY 1 35026 #define MA_SIO_REC 2 35027 #define MA_SIO_NENC 8 35028 #define MA_SIO_NCHAN 8 35029 #define MA_SIO_NRATE 16 35030 #define MA_SIO_NCONF 4 35031 35032 struct ma_sio_hdl; /* <-- Opaque */ 35033 35034 struct ma_sio_par 35035 { 35036 unsigned int bits; 35037 unsigned int bps; 35038 unsigned int sig; 35039 unsigned int le; 35040 unsigned int msb; 35041 unsigned int rchan; 35042 unsigned int pchan; 35043 unsigned int rate; 35044 unsigned int bufsz; 35045 unsigned int xrun; 35046 unsigned int round; 35047 unsigned int appbufsz; 35048 int __pad[3]; 35049 unsigned int __magic; 35050 }; 35051 35052 struct ma_sio_enc 35053 { 35054 unsigned int bits; 35055 unsigned int bps; 35056 unsigned int sig; 35057 unsigned int le; 35058 unsigned int msb; 35059 }; 35060 35061 struct ma_sio_conf 35062 { 35063 unsigned int enc; 35064 unsigned int rchan; 35065 unsigned int pchan; 35066 unsigned int rate; 35067 }; 35068 35069 struct ma_sio_cap 35070 { 35071 struct ma_sio_enc enc[MA_SIO_NENC]; 35072 unsigned int rchan[MA_SIO_NCHAN]; 35073 unsigned int pchan[MA_SIO_NCHAN]; 35074 unsigned int rate[MA_SIO_NRATE]; 35075 int __pad[7]; 35076 unsigned int nconf; 35077 struct ma_sio_conf confs[MA_SIO_NCONF]; 35078 }; 35079 35080 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); 35081 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); 35082 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); 35083 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); 35084 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); 35085 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); 35086 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); 35087 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); 35088 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); 35089 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); 35090 35091 static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ 35092 { 35093 ma_uint32 i; 35094 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { 35095 if (g_maStandardSampleRatePriorities[i] == sampleRate) { 35096 return i; 35097 } 35098 } 35099 35100 return (ma_uint32)-1; 35101 } 35102 35103 static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) 35104 { 35105 /* We only support native-endian right now. */ 35106 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { 35107 return ma_format_unknown; 35108 } 35109 35110 if (bits == 8 && bps == 1 && sig == 0) { 35111 return ma_format_u8; 35112 } 35113 if (bits == 16 && bps == 2 && sig == 1) { 35114 return ma_format_s16; 35115 } 35116 if (bits == 24 && bps == 3 && sig == 1) { 35117 return ma_format_s24; 35118 } 35119 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { 35120 /*return ma_format_s24_32;*/ 35121 } 35122 if (bits == 32 && bps == 4 && sig == 1) { 35123 return ma_format_s32; 35124 } 35125 35126 return ma_format_unknown; 35127 } 35128 35129 static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) 35130 { 35131 ma_format bestFormat; 35132 unsigned int iConfig; 35133 35134 MA_ASSERT(caps != NULL); 35135 35136 bestFormat = ma_format_unknown; 35137 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { 35138 unsigned int iEncoding; 35139 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { 35140 unsigned int bits; 35141 unsigned int bps; 35142 unsigned int sig; 35143 unsigned int le; 35144 unsigned int msb; 35145 ma_format format; 35146 35147 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { 35148 continue; 35149 } 35150 35151 bits = caps->enc[iEncoding].bits; 35152 bps = caps->enc[iEncoding].bps; 35153 sig = caps->enc[iEncoding].sig; 35154 le = caps->enc[iEncoding].le; 35155 msb = caps->enc[iEncoding].msb; 35156 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); 35157 if (format == ma_format_unknown) { 35158 continue; /* Format not supported. */ 35159 } 35160 35161 if (bestFormat == ma_format_unknown) { 35162 bestFormat = format; 35163 } else { 35164 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ 35165 bestFormat = format; 35166 } 35167 } 35168 } 35169 } 35170 35171 return bestFormat; 35172 } 35173 35174 static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) 35175 { 35176 ma_uint32 maxChannels; 35177 unsigned int iConfig; 35178 35179 MA_ASSERT(caps != NULL); 35180 MA_ASSERT(requiredFormat != ma_format_unknown); 35181 35182 /* Just pick whatever configuration has the most channels. */ 35183 maxChannels = 0; 35184 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { 35185 /* The encoding should be of requiredFormat. */ 35186 unsigned int iEncoding; 35187 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { 35188 unsigned int iChannel; 35189 unsigned int bits; 35190 unsigned int bps; 35191 unsigned int sig; 35192 unsigned int le; 35193 unsigned int msb; 35194 ma_format format; 35195 35196 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { 35197 continue; 35198 } 35199 35200 bits = caps->enc[iEncoding].bits; 35201 bps = caps->enc[iEncoding].bps; 35202 sig = caps->enc[iEncoding].sig; 35203 le = caps->enc[iEncoding].le; 35204 msb = caps->enc[iEncoding].msb; 35205 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); 35206 if (format != requiredFormat) { 35207 continue; 35208 } 35209 35210 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ 35211 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { 35212 unsigned int chan = 0; 35213 unsigned int channels; 35214 35215 if (deviceType == ma_device_type_playback) { 35216 chan = caps->confs[iConfig].pchan; 35217 } else { 35218 chan = caps->confs[iConfig].rchan; 35219 } 35220 35221 if ((chan & (1UL << iChannel)) == 0) { 35222 continue; 35223 } 35224 35225 if (deviceType == ma_device_type_playback) { 35226 channels = caps->pchan[iChannel]; 35227 } else { 35228 channels = caps->rchan[iChannel]; 35229 } 35230 35231 if (maxChannels < channels) { 35232 maxChannels = channels; 35233 } 35234 } 35235 } 35236 } 35237 35238 return maxChannels; 35239 } 35240 35241 static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) 35242 { 35243 ma_uint32 firstSampleRate; 35244 ma_uint32 bestSampleRate; 35245 unsigned int iConfig; 35246 35247 MA_ASSERT(caps != NULL); 35248 MA_ASSERT(requiredFormat != ma_format_unknown); 35249 MA_ASSERT(requiredChannels > 0); 35250 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); 35251 35252 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ 35253 bestSampleRate = 0; 35254 35255 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { 35256 /* The encoding should be of requiredFormat. */ 35257 unsigned int iEncoding; 35258 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { 35259 unsigned int iChannel; 35260 unsigned int bits; 35261 unsigned int bps; 35262 unsigned int sig; 35263 unsigned int le; 35264 unsigned int msb; 35265 ma_format format; 35266 35267 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { 35268 continue; 35269 } 35270 35271 bits = caps->enc[iEncoding].bits; 35272 bps = caps->enc[iEncoding].bps; 35273 sig = caps->enc[iEncoding].sig; 35274 le = caps->enc[iEncoding].le; 35275 msb = caps->enc[iEncoding].msb; 35276 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); 35277 if (format != requiredFormat) { 35278 continue; 35279 } 35280 35281 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ 35282 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { 35283 unsigned int chan = 0; 35284 unsigned int channels; 35285 unsigned int iRate; 35286 35287 if (deviceType == ma_device_type_playback) { 35288 chan = caps->confs[iConfig].pchan; 35289 } else { 35290 chan = caps->confs[iConfig].rchan; 35291 } 35292 35293 if ((chan & (1UL << iChannel)) == 0) { 35294 continue; 35295 } 35296 35297 if (deviceType == ma_device_type_playback) { 35298 channels = caps->pchan[iChannel]; 35299 } else { 35300 channels = caps->rchan[iChannel]; 35301 } 35302 35303 if (channels != requiredChannels) { 35304 continue; 35305 } 35306 35307 /* Getting here means we have found a compatible encoding/channel pair. */ 35308 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { 35309 ma_uint32 rate = (ma_uint32)caps->rate[iRate]; 35310 ma_uint32 ratePriority; 35311 35312 if (firstSampleRate == 0) { 35313 firstSampleRate = rate; 35314 } 35315 35316 /* Disregard this rate if it's not a standard one. */ 35317 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); 35318 if (ratePriority == (ma_uint32)-1) { 35319 continue; 35320 } 35321 35322 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ 35323 bestSampleRate = rate; 35324 } 35325 } 35326 } 35327 } 35328 } 35329 35330 /* If a standard sample rate was not found just fall back to the first one that was iterated. */ 35331 if (bestSampleRate == 0) { 35332 bestSampleRate = firstSampleRate; 35333 } 35334 35335 return bestSampleRate; 35336 } 35337 35338 35339 static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 35340 { 35341 ma_bool32 isTerminating = MA_FALSE; 35342 struct ma_sio_hdl* handle; 35343 35344 MA_ASSERT(pContext != NULL); 35345 MA_ASSERT(callback != NULL); 35346 35347 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ 35348 35349 /* Playback. */ 35350 if (!isTerminating) { 35351 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); 35352 if (handle != NULL) { 35353 /* Supports playback. */ 35354 ma_device_info deviceInfo; 35355 MA_ZERO_OBJECT(&deviceInfo); 35356 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); 35357 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); 35358 35359 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 35360 35361 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); 35362 } 35363 } 35364 35365 /* Capture. */ 35366 if (!isTerminating) { 35367 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); 35368 if (handle != NULL) { 35369 /* Supports capture. */ 35370 ma_device_info deviceInfo; 35371 MA_ZERO_OBJECT(&deviceInfo); 35372 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); 35373 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); 35374 35375 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 35376 35377 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); 35378 } 35379 } 35380 35381 return MA_SUCCESS; 35382 } 35383 35384 static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 35385 { 35386 char devid[256]; 35387 struct ma_sio_hdl* handle; 35388 struct ma_sio_cap caps; 35389 unsigned int iConfig; 35390 35391 MA_ASSERT(pContext != NULL); 35392 35393 /* We need to open the device before we can get information about it. */ 35394 if (pDeviceID == NULL) { 35395 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); 35396 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); 35397 } else { 35398 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); 35399 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); 35400 } 35401 35402 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); 35403 if (handle == NULL) { 35404 return MA_NO_DEVICE; 35405 } 35406 35407 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { 35408 return MA_ERROR; 35409 } 35410 35411 pDeviceInfo->nativeDataFormatCount = 0; 35412 35413 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { 35414 /* 35415 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give 35416 preference to some formats over others. 35417 */ 35418 unsigned int iEncoding; 35419 unsigned int iChannel; 35420 unsigned int iRate; 35421 35422 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { 35423 unsigned int bits; 35424 unsigned int bps; 35425 unsigned int sig; 35426 unsigned int le; 35427 unsigned int msb; 35428 ma_format format; 35429 35430 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { 35431 continue; 35432 } 35433 35434 bits = caps.enc[iEncoding].bits; 35435 bps = caps.enc[iEncoding].bps; 35436 sig = caps.enc[iEncoding].sig; 35437 le = caps.enc[iEncoding].le; 35438 msb = caps.enc[iEncoding].msb; 35439 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); 35440 if (format == ma_format_unknown) { 35441 continue; /* Format not supported. */ 35442 } 35443 35444 35445 /* Channels. */ 35446 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { 35447 unsigned int chan = 0; 35448 unsigned int channels; 35449 35450 if (deviceType == ma_device_type_playback) { 35451 chan = caps.confs[iConfig].pchan; 35452 } else { 35453 chan = caps.confs[iConfig].rchan; 35454 } 35455 35456 if ((chan & (1UL << iChannel)) == 0) { 35457 continue; 35458 } 35459 35460 if (deviceType == ma_device_type_playback) { 35461 channels = caps.pchan[iChannel]; 35462 } else { 35463 channels = caps.rchan[iChannel]; 35464 } 35465 35466 35467 /* Sample Rates. */ 35468 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { 35469 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { 35470 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); 35471 } 35472 } 35473 } 35474 } 35475 } 35476 35477 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); 35478 return MA_SUCCESS; 35479 } 35480 35481 static ma_result ma_device_uninit__sndio(ma_device* pDevice) 35482 { 35483 MA_ASSERT(pDevice != NULL); 35484 35485 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 35486 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); 35487 } 35488 35489 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 35490 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); 35491 } 35492 35493 return MA_SUCCESS; 35494 } 35495 35496 static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) 35497 { 35498 const char* pDeviceName; 35499 ma_ptr handle; 35500 int openFlags = 0; 35501 struct ma_sio_cap caps; 35502 struct ma_sio_par par; 35503 const ma_device_id* pDeviceID; 35504 ma_format format; 35505 ma_uint32 channels; 35506 ma_uint32 sampleRate; 35507 ma_format internalFormat; 35508 ma_uint32 internalChannels; 35509 ma_uint32 internalSampleRate; 35510 ma_uint32 internalPeriodSizeInFrames; 35511 ma_uint32 internalPeriods; 35512 35513 MA_ASSERT(pConfig != NULL); 35514 MA_ASSERT(deviceType != ma_device_type_duplex); 35515 MA_ASSERT(pDevice != NULL); 35516 35517 if (deviceType == ma_device_type_capture) { 35518 openFlags = MA_SIO_REC; 35519 } else { 35520 openFlags = MA_SIO_PLAY; 35521 } 35522 35523 pDeviceID = pDescriptor->pDeviceID; 35524 format = pDescriptor->format; 35525 channels = pDescriptor->channels; 35526 sampleRate = pDescriptor->sampleRate; 35527 35528 pDeviceName = MA_SIO_DEVANY; 35529 if (pDeviceID != NULL) { 35530 pDeviceName = pDeviceID->sndio; 35531 } 35532 35533 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); 35534 if (handle == NULL) { 35535 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); 35536 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 35537 } 35538 35539 /* We need to retrieve the device caps to determine the most appropriate format to use. */ 35540 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { 35541 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); 35542 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); 35543 return MA_ERROR; 35544 } 35545 35546 /* 35547 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real 35548 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this 35549 to the requested channels, regardless of whether or not the default channel count is requested. 35550 35551 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the 35552 value returned by ma_find_best_channels_from_sio_cap__sndio(). 35553 */ 35554 if (deviceType == ma_device_type_capture) { 35555 if (format == ma_format_unknown) { 35556 format = ma_find_best_format_from_sio_cap__sndio(&caps); 35557 } 35558 35559 if (channels == 0) { 35560 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { 35561 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); 35562 } else { 35563 channels = MA_DEFAULT_CHANNELS; 35564 } 35565 } 35566 } else { 35567 if (format == ma_format_unknown) { 35568 format = ma_find_best_format_from_sio_cap__sndio(&caps); 35569 } 35570 35571 if (channels == 0) { 35572 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { 35573 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); 35574 } else { 35575 channels = MA_DEFAULT_CHANNELS; 35576 } 35577 } 35578 } 35579 35580 if (sampleRate == 0) { 35581 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); 35582 } 35583 35584 35585 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); 35586 par.msb = 0; 35587 par.le = ma_is_little_endian(); 35588 35589 switch (format) { 35590 case ma_format_u8: 35591 { 35592 par.bits = 8; 35593 par.bps = 1; 35594 par.sig = 0; 35595 } break; 35596 35597 case ma_format_s24: 35598 { 35599 par.bits = 24; 35600 par.bps = 3; 35601 par.sig = 1; 35602 } break; 35603 35604 case ma_format_s32: 35605 { 35606 par.bits = 32; 35607 par.bps = 4; 35608 par.sig = 1; 35609 } break; 35610 35611 case ma_format_s16: 35612 case ma_format_f32: 35613 case ma_format_unknown: 35614 default: 35615 { 35616 par.bits = 16; 35617 par.bps = 2; 35618 par.sig = 1; 35619 } break; 35620 } 35621 35622 if (deviceType == ma_device_type_capture) { 35623 par.rchan = channels; 35624 } else { 35625 par.pchan = channels; 35626 } 35627 35628 par.rate = sampleRate; 35629 35630 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); 35631 35632 par.round = internalPeriodSizeInFrames; 35633 par.appbufsz = par.round * pDescriptor->periodCount; 35634 35635 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { 35636 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); 35637 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); 35638 return MA_ERROR; 35639 } 35640 35641 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { 35642 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); 35643 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); 35644 return MA_ERROR; 35645 } 35646 35647 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); 35648 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; 35649 internalSampleRate = par.rate; 35650 internalPeriods = par.appbufsz / par.round; 35651 internalPeriodSizeInFrames = par.round; 35652 35653 if (deviceType == ma_device_type_capture) { 35654 pDevice->sndio.handleCapture = handle; 35655 } else { 35656 pDevice->sndio.handlePlayback = handle; 35657 } 35658 35659 pDescriptor->format = internalFormat; 35660 pDescriptor->channels = internalChannels; 35661 pDescriptor->sampleRate = internalSampleRate; 35662 ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); 35663 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; 35664 pDescriptor->periodCount = internalPeriods; 35665 35666 return MA_SUCCESS; 35667 } 35668 35669 static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 35670 { 35671 MA_ASSERT(pDevice != NULL); 35672 35673 MA_ZERO_OBJECT(&pDevice->sndio); 35674 35675 if (pConfig->deviceType == ma_device_type_loopback) { 35676 return MA_DEVICE_TYPE_NOT_SUPPORTED; 35677 } 35678 35679 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 35680 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); 35681 if (result != MA_SUCCESS) { 35682 return result; 35683 } 35684 } 35685 35686 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 35687 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); 35688 if (result != MA_SUCCESS) { 35689 return result; 35690 } 35691 } 35692 35693 return MA_SUCCESS; 35694 } 35695 35696 static ma_result ma_device_start__sndio(ma_device* pDevice) 35697 { 35698 MA_ASSERT(pDevice != NULL); 35699 35700 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 35701 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); 35702 } 35703 35704 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 35705 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ 35706 } 35707 35708 return MA_SUCCESS; 35709 } 35710 35711 static ma_result ma_device_stop__sndio(ma_device* pDevice) 35712 { 35713 MA_ASSERT(pDevice != NULL); 35714 35715 /* 35716 From the documentation: 35717 35718 The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then 35719 stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the 35720 buffer is drained. In no case are samples in the play buffer discarded. 35721 35722 Therefore, sio_stop() performs all of the necessary draining for us. 35723 */ 35724 35725 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 35726 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); 35727 } 35728 35729 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 35730 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); 35731 } 35732 35733 return MA_SUCCESS; 35734 } 35735 35736 static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 35737 { 35738 int result; 35739 35740 if (pFramesWritten != NULL) { 35741 *pFramesWritten = 0; 35742 } 35743 35744 result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 35745 if (result == 0) { 35746 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); 35747 return MA_IO_ERROR; 35748 } 35749 35750 if (pFramesWritten != NULL) { 35751 *pFramesWritten = frameCount; 35752 } 35753 35754 return MA_SUCCESS; 35755 } 35756 35757 static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 35758 { 35759 int result; 35760 35761 if (pFramesRead != NULL) { 35762 *pFramesRead = 0; 35763 } 35764 35765 result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 35766 if (result == 0) { 35767 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); 35768 return MA_IO_ERROR; 35769 } 35770 35771 if (pFramesRead != NULL) { 35772 *pFramesRead = frameCount; 35773 } 35774 35775 return MA_SUCCESS; 35776 } 35777 35778 static ma_result ma_context_uninit__sndio(ma_context* pContext) 35779 { 35780 MA_ASSERT(pContext != NULL); 35781 MA_ASSERT(pContext->backend == ma_backend_sndio); 35782 35783 (void)pContext; 35784 return MA_SUCCESS; 35785 } 35786 35787 static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 35788 { 35789 #ifndef MA_NO_RUNTIME_LINKING 35790 const char* libsndioNames[] = { 35791 "libsndio.so" 35792 }; 35793 size_t i; 35794 35795 for (i = 0; i < ma_countof(libsndioNames); ++i) { 35796 pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); 35797 if (pContext->sndio.sndioSO != NULL) { 35798 break; 35799 } 35800 } 35801 35802 if (pContext->sndio.sndioSO == NULL) { 35803 return MA_NO_BACKEND; 35804 } 35805 35806 pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); 35807 pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); 35808 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); 35809 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); 35810 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); 35811 pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); 35812 pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); 35813 pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); 35814 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); 35815 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); 35816 #else 35817 pContext->sndio.sio_open = sio_open; 35818 pContext->sndio.sio_close = sio_close; 35819 pContext->sndio.sio_setpar = sio_setpar; 35820 pContext->sndio.sio_getpar = sio_getpar; 35821 pContext->sndio.sio_getcap = sio_getcap; 35822 pContext->sndio.sio_write = sio_write; 35823 pContext->sndio.sio_read = sio_read; 35824 pContext->sndio.sio_start = sio_start; 35825 pContext->sndio.sio_stop = sio_stop; 35826 pContext->sndio.sio_initpar = sio_initpar; 35827 #endif 35828 35829 pCallbacks->onContextInit = ma_context_init__sndio; 35830 pCallbacks->onContextUninit = ma_context_uninit__sndio; 35831 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; 35832 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; 35833 pCallbacks->onDeviceInit = ma_device_init__sndio; 35834 pCallbacks->onDeviceUninit = ma_device_uninit__sndio; 35835 pCallbacks->onDeviceStart = ma_device_start__sndio; 35836 pCallbacks->onDeviceStop = ma_device_stop__sndio; 35837 pCallbacks->onDeviceRead = ma_device_read__sndio; 35838 pCallbacks->onDeviceWrite = ma_device_write__sndio; 35839 pCallbacks->onDeviceDataLoop = NULL; 35840 35841 (void)pConfig; 35842 return MA_SUCCESS; 35843 } 35844 #endif /* sndio */ 35845 35846 35847 35848 /****************************************************************************** 35849 35850 audio(4) Backend 35851 35852 ******************************************************************************/ 35853 #ifdef MA_HAS_AUDIO4 35854 #include <fcntl.h> 35855 #include <poll.h> 35856 #include <errno.h> 35857 #include <sys/stat.h> 35858 #include <sys/types.h> 35859 #include <sys/ioctl.h> 35860 #include <sys/audioio.h> 35861 35862 #if defined(__OpenBSD__) 35863 #include <sys/param.h> 35864 #if defined(OpenBSD) && OpenBSD >= 201709 35865 #define MA_AUDIO4_USE_NEW_API 35866 #endif 35867 #endif 35868 35869 static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) 35870 { 35871 size_t baseLen; 35872 35873 MA_ASSERT(id != NULL); 35874 MA_ASSERT(idSize > 0); 35875 MA_ASSERT(deviceIndex >= 0); 35876 35877 baseLen = strlen(base); 35878 MA_ASSERT(idSize > baseLen); 35879 35880 ma_strcpy_s(id, idSize, base); 35881 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); 35882 } 35883 35884 static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) 35885 { 35886 size_t idLen; 35887 size_t baseLen; 35888 const char* deviceIndexStr; 35889 35890 MA_ASSERT(id != NULL); 35891 MA_ASSERT(base != NULL); 35892 MA_ASSERT(pIndexOut != NULL); 35893 35894 idLen = strlen(id); 35895 baseLen = strlen(base); 35896 if (idLen <= baseLen) { 35897 return MA_ERROR; /* Doesn't look like the id starts with the base. */ 35898 } 35899 35900 if (strncmp(id, base, baseLen) != 0) { 35901 return MA_ERROR; /* ID does not begin with base. */ 35902 } 35903 35904 deviceIndexStr = id + baseLen; 35905 if (deviceIndexStr[0] == '\0') { 35906 return MA_ERROR; /* No index specified in the ID. */ 35907 } 35908 35909 if (pIndexOut) { 35910 *pIndexOut = atoi(deviceIndexStr); 35911 } 35912 35913 return MA_SUCCESS; 35914 } 35915 35916 35917 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ 35918 static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) 35919 { 35920 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { 35921 return ma_format_u8; 35922 } else { 35923 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { 35924 if (precision == 16) { 35925 return ma_format_s16; 35926 } else if (precision == 24) { 35927 return ma_format_s24; 35928 } else if (precision == 32) { 35929 return ma_format_s32; 35930 } 35931 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { 35932 if (precision == 16) { 35933 return ma_format_s16; 35934 } else if (precision == 24) { 35935 return ma_format_s24; 35936 } else if (precision == 32) { 35937 return ma_format_s32; 35938 } 35939 } 35940 } 35941 35942 return ma_format_unknown; /* Encoding not supported. */ 35943 } 35944 35945 static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) 35946 { 35947 MA_ASSERT(pEncoding != NULL); 35948 MA_ASSERT(pPrecision != NULL); 35949 35950 switch (format) 35951 { 35952 case ma_format_u8: 35953 { 35954 *pEncoding = AUDIO_ENCODING_ULINEAR; 35955 *pPrecision = 8; 35956 } break; 35957 35958 case ma_format_s24: 35959 { 35960 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; 35961 *pPrecision = 24; 35962 } break; 35963 35964 case ma_format_s32: 35965 { 35966 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; 35967 *pPrecision = 32; 35968 } break; 35969 35970 case ma_format_s16: 35971 case ma_format_f32: 35972 case ma_format_unknown: 35973 default: 35974 { 35975 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; 35976 *pPrecision = 16; 35977 } break; 35978 } 35979 } 35980 35981 static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) 35982 { 35983 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); 35984 } 35985 35986 static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) 35987 { 35988 audio_encoding_t encoding; 35989 ma_uint32 iFormat; 35990 int counter = 0; 35991 35992 /* First check to see if the preferred format is supported. */ 35993 if (preferredFormat != ma_format_unknown) { 35994 counter = 0; 35995 for (;;) { 35996 MA_ZERO_OBJECT(&encoding); 35997 encoding.index = counter; 35998 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { 35999 break; 36000 } 36001 36002 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { 36003 return preferredFormat; /* Found the preferred format. */ 36004 } 36005 36006 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ 36007 counter += 1; 36008 } 36009 } 36010 36011 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ 36012 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { 36013 ma_format format = g_maFormatPriorities[iFormat]; 36014 36015 counter = 0; 36016 for (;;) { 36017 MA_ZERO_OBJECT(&encoding); 36018 encoding.index = counter; 36019 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { 36020 break; 36021 } 36022 36023 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { 36024 return format; /* Found a workable format. */ 36025 } 36026 36027 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ 36028 counter += 1; 36029 } 36030 } 36031 36032 /* Getting here means not appropriate format was found. */ 36033 return ma_format_unknown; 36034 } 36035 #else 36036 static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) 36037 { 36038 if (par->bits == 8 && par->bps == 1 && par->sig == 0) { 36039 return ma_format_u8; 36040 } 36041 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { 36042 return ma_format_s16; 36043 } 36044 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { 36045 return ma_format_s24; 36046 } 36047 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { 36048 return ma_format_f32; 36049 } 36050 36051 /* Format not supported. */ 36052 return ma_format_unknown; 36053 } 36054 #endif 36055 36056 static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) 36057 { 36058 audio_device_t fdDevice; 36059 36060 MA_ASSERT(pContext != NULL); 36061 MA_ASSERT(fd >= 0); 36062 MA_ASSERT(pDeviceInfo != NULL); 36063 36064 (void)pContext; 36065 (void)deviceType; 36066 36067 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { 36068 return MA_ERROR; /* Failed to retrieve device info. */ 36069 } 36070 36071 /* Name. */ 36072 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); 36073 36074 #if !defined(MA_AUDIO4_USE_NEW_API) 36075 { 36076 audio_info_t fdInfo; 36077 int counter = 0; 36078 ma_uint32 channels; 36079 ma_uint32 sampleRate; 36080 36081 #ifdef __NetBSD__ 36082 if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { 36083 return MA_ERROR; 36084 } 36085 #else 36086 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { 36087 return MA_ERROR; 36088 } 36089 #endif 36090 36091 if (deviceType == ma_device_type_playback) { 36092 channels = fdInfo.play.channels; 36093 sampleRate = fdInfo.play.sample_rate; 36094 } else { 36095 channels = fdInfo.record.channels; 36096 sampleRate = fdInfo.record.sample_rate; 36097 } 36098 36099 /* Supported formats. We get this by looking at the encodings. */ 36100 pDeviceInfo->nativeDataFormatCount = 0; 36101 for (;;) { 36102 audio_encoding_t encoding; 36103 ma_format format; 36104 36105 MA_ZERO_OBJECT(&encoding); 36106 encoding.index = counter; 36107 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { 36108 break; 36109 } 36110 36111 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); 36112 if (format != ma_format_unknown) { 36113 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); 36114 } 36115 36116 counter += 1; 36117 } 36118 } 36119 #else 36120 { 36121 struct audio_swpar fdPar; 36122 ma_format format; 36123 ma_uint32 channels; 36124 ma_uint32 sampleRate; 36125 36126 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { 36127 return MA_ERROR; 36128 } 36129 36130 format = ma_format_from_swpar__audio4(&fdPar); 36131 if (format == ma_format_unknown) { 36132 return MA_FORMAT_NOT_SUPPORTED; 36133 } 36134 36135 if (deviceType == ma_device_type_playback) { 36136 channels = fdPar.pchan; 36137 } else { 36138 channels = fdPar.rchan; 36139 } 36140 36141 sampleRate = fdPar.rate; 36142 36143 pDeviceInfo->nativeDataFormatCount = 0; 36144 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); 36145 } 36146 #endif 36147 36148 return MA_SUCCESS; 36149 } 36150 36151 static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 36152 { 36153 const int maxDevices = 64; 36154 char devpath[256]; 36155 int iDevice; 36156 36157 MA_ASSERT(pContext != NULL); 36158 MA_ASSERT(callback != NULL); 36159 36160 /* 36161 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" 36162 version here since we can open it even when another process has control of the "/dev/audioN" device. 36163 */ 36164 for (iDevice = 0; iDevice < maxDevices; ++iDevice) { 36165 struct stat st; 36166 int fd; 36167 ma_bool32 isTerminating = MA_FALSE; 36168 36169 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); 36170 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); 36171 36172 if (stat(devpath, &st) < 0) { 36173 break; 36174 } 36175 36176 /* The device exists, but we need to check if it's usable as playback and/or capture. */ 36177 36178 /* Playback. */ 36179 if (!isTerminating) { 36180 fd = open(devpath, O_RDONLY, 0); 36181 if (fd >= 0) { 36182 /* Supports playback. */ 36183 ma_device_info deviceInfo; 36184 MA_ZERO_OBJECT(&deviceInfo); 36185 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); 36186 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { 36187 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 36188 } 36189 36190 close(fd); 36191 } 36192 } 36193 36194 /* Capture. */ 36195 if (!isTerminating) { 36196 fd = open(devpath, O_WRONLY, 0); 36197 if (fd >= 0) { 36198 /* Supports capture. */ 36199 ma_device_info deviceInfo; 36200 MA_ZERO_OBJECT(&deviceInfo); 36201 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); 36202 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { 36203 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 36204 } 36205 36206 close(fd); 36207 } 36208 } 36209 36210 if (isTerminating) { 36211 break; 36212 } 36213 } 36214 36215 return MA_SUCCESS; 36216 } 36217 36218 static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 36219 { 36220 int fd = -1; 36221 int deviceIndex = -1; 36222 char ctlid[256]; 36223 ma_result result; 36224 36225 MA_ASSERT(pContext != NULL); 36226 36227 /* 36228 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number 36229 from the device ID which will be in "/dev/audioN" format. 36230 */ 36231 if (pDeviceID == NULL) { 36232 /* Default device. */ 36233 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); 36234 } else { 36235 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ 36236 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); 36237 if (result != MA_SUCCESS) { 36238 return result; 36239 } 36240 36241 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); 36242 } 36243 36244 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); 36245 if (fd == -1) { 36246 return MA_NO_DEVICE; 36247 } 36248 36249 if (deviceIndex == -1) { 36250 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); 36251 } else { 36252 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); 36253 } 36254 36255 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); 36256 36257 close(fd); 36258 return result; 36259 } 36260 36261 static ma_result ma_device_uninit__audio4(ma_device* pDevice) 36262 { 36263 MA_ASSERT(pDevice != NULL); 36264 36265 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 36266 close(pDevice->audio4.fdCapture); 36267 } 36268 36269 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 36270 close(pDevice->audio4.fdPlayback); 36271 } 36272 36273 return MA_SUCCESS; 36274 } 36275 36276 static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) 36277 { 36278 const char* pDefaultDeviceNames[] = { 36279 "/dev/audio", 36280 "/dev/audio0" 36281 }; 36282 const char* pDefaultDeviceCtlNames[] = { 36283 "/dev/audioctl", 36284 "/dev/audioctl0" 36285 }; 36286 int fd; 36287 int fdFlags = 0; 36288 size_t iDefaultDevice = (size_t)-1; 36289 ma_format internalFormat; 36290 ma_uint32 internalChannels; 36291 ma_uint32 internalSampleRate; 36292 ma_uint32 internalPeriodSizeInFrames; 36293 ma_uint32 internalPeriods; 36294 36295 MA_ASSERT(pConfig != NULL); 36296 MA_ASSERT(deviceType != ma_device_type_duplex); 36297 MA_ASSERT(pDevice != NULL); 36298 36299 /* The first thing to do is open the file. */ 36300 if (deviceType == ma_device_type_capture) { 36301 fdFlags = O_RDONLY; 36302 } else { 36303 fdFlags = O_WRONLY; 36304 } 36305 /*fdFlags |= O_NONBLOCK;*/ 36306 36307 /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ 36308 if (pDescriptor->pDeviceID == NULL) { 36309 /* Default device. */ 36310 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { 36311 fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); 36312 if (fd != -1) { 36313 break; 36314 } 36315 } 36316 } else { 36317 /* Specific device. */ 36318 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); 36319 36320 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { 36321 if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { 36322 break; 36323 } 36324 } 36325 36326 if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { 36327 iDefaultDevice = (size_t)-1; 36328 } 36329 } 36330 36331 if (fd == -1) { 36332 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); 36333 return ma_result_from_errno(errno); 36334 } 36335 36336 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ 36337 { 36338 audio_info_t fdInfo; 36339 int fdInfoResult = -1; 36340 36341 /* 36342 The documentation is a little bit unclear to me as to how it handles formats. It says the 36343 following: 36344 36345 Regardless of formats supported by underlying driver, the audio driver accepts the 36346 following formats. 36347 36348 By then the next sentence says this: 36349 36350 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. 36351 36352 It sounds like a direct contradiction to me. I'm going to play this safe any only use the 36353 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll 36354 use that, but otherwise we'll just use our standard format priorities to pick an 36355 appropriate one. 36356 */ 36357 AUDIO_INITINFO(&fdInfo); 36358 36359 /* 36360 Get the default format from the audioctl file if we're asking for a default device. If we 36361 retrieve it from /dev/audio it'll default to mono 8000Hz. 36362 */ 36363 if (iDefaultDevice != (size_t)-1) { 36364 /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ 36365 int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); 36366 if (fdctl != -1) { 36367 #ifdef __NetBSD__ 36368 fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); 36369 #else 36370 fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); 36371 #endif 36372 close(fdctl); 36373 } 36374 } 36375 36376 if (fdInfoResult == -1) { 36377 /* We still don't have the default device info so just retrieve it from the main audio device. */ 36378 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { 36379 close(fd); 36380 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); 36381 return ma_result_from_errno(errno); 36382 } 36383 } 36384 36385 /* We get the driver to do as much of the data conversion as possible. */ 36386 if (deviceType == ma_device_type_capture) { 36387 fdInfo.mode = AUMODE_RECORD; 36388 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); 36389 36390 if (pDescriptor->channels != 0) { 36391 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ 36392 } 36393 36394 if (pDescriptor->sampleRate != 0) { 36395 fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ 36396 } 36397 } else { 36398 fdInfo.mode = AUMODE_PLAY; 36399 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); 36400 36401 if (pDescriptor->channels != 0) { 36402 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ 36403 } 36404 36405 if (pDescriptor->sampleRate != 0) { 36406 fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ 36407 } 36408 } 36409 36410 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { 36411 close(fd); 36412 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); 36413 return ma_result_from_errno(errno); 36414 } 36415 36416 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { 36417 close(fd); 36418 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); 36419 return ma_result_from_errno(errno); 36420 } 36421 36422 if (deviceType == ma_device_type_capture) { 36423 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); 36424 internalChannels = fdInfo.record.channels; 36425 internalSampleRate = fdInfo.record.sample_rate; 36426 } else { 36427 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); 36428 internalChannels = fdInfo.play.channels; 36429 internalSampleRate = fdInfo.play.sample_rate; 36430 } 36431 36432 if (internalFormat == ma_format_unknown) { 36433 close(fd); 36434 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); 36435 return MA_FORMAT_NOT_SUPPORTED; 36436 } 36437 36438 /* Buffer. */ 36439 { 36440 ma_uint32 internalPeriodSizeInBytes; 36441 36442 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); 36443 36444 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); 36445 if (internalPeriodSizeInBytes < 16) { 36446 internalPeriodSizeInBytes = 16; 36447 } 36448 36449 internalPeriods = pDescriptor->periodCount; 36450 if (internalPeriods < 2) { 36451 internalPeriods = 2; 36452 } 36453 36454 /* What miniaudio calls a period, audio4 calls a block. */ 36455 AUDIO_INITINFO(&fdInfo); 36456 fdInfo.hiwat = internalPeriods; 36457 fdInfo.lowat = internalPeriods-1; 36458 fdInfo.blocksize = internalPeriodSizeInBytes; 36459 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { 36460 close(fd); 36461 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); 36462 return ma_result_from_errno(errno); 36463 } 36464 36465 internalPeriods = fdInfo.hiwat; 36466 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); 36467 } 36468 } 36469 #else 36470 { 36471 struct audio_swpar fdPar; 36472 36473 /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ 36474 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { 36475 close(fd); 36476 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); 36477 return ma_result_from_errno(errno); 36478 } 36479 36480 internalFormat = ma_format_from_swpar__audio4(&fdPar); 36481 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; 36482 internalSampleRate = fdPar.rate; 36483 36484 if (internalFormat == ma_format_unknown) { 36485 close(fd); 36486 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); 36487 return MA_FORMAT_NOT_SUPPORTED; 36488 } 36489 36490 /* Buffer. */ 36491 { 36492 ma_uint32 internalPeriodSizeInBytes; 36493 36494 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); 36495 36496 /* What miniaudio calls a period, audio4 calls a block. */ 36497 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); 36498 if (internalPeriodSizeInBytes < 16) { 36499 internalPeriodSizeInBytes = 16; 36500 } 36501 36502 fdPar.nblks = pDescriptor->periodCount; 36503 fdPar.round = internalPeriodSizeInBytes; 36504 36505 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { 36506 close(fd); 36507 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); 36508 return ma_result_from_errno(errno); 36509 } 36510 36511 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { 36512 close(fd); 36513 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); 36514 return ma_result_from_errno(errno); 36515 } 36516 } 36517 36518 internalFormat = ma_format_from_swpar__audio4(&fdPar); 36519 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; 36520 internalSampleRate = fdPar.rate; 36521 internalPeriods = fdPar.nblks; 36522 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); 36523 } 36524 #endif 36525 36526 if (internalFormat == ma_format_unknown) { 36527 close(fd); 36528 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); 36529 return MA_FORMAT_NOT_SUPPORTED; 36530 } 36531 36532 if (deviceType == ma_device_type_capture) { 36533 pDevice->audio4.fdCapture = fd; 36534 } else { 36535 pDevice->audio4.fdPlayback = fd; 36536 } 36537 36538 pDescriptor->format = internalFormat; 36539 pDescriptor->channels = internalChannels; 36540 pDescriptor->sampleRate = internalSampleRate; 36541 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); 36542 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; 36543 pDescriptor->periodCount = internalPeriods; 36544 36545 return MA_SUCCESS; 36546 } 36547 36548 static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 36549 { 36550 MA_ASSERT(pDevice != NULL); 36551 36552 MA_ZERO_OBJECT(&pDevice->audio4); 36553 36554 if (pConfig->deviceType == ma_device_type_loopback) { 36555 return MA_DEVICE_TYPE_NOT_SUPPORTED; 36556 } 36557 36558 pDevice->audio4.fdCapture = -1; 36559 pDevice->audio4.fdPlayback = -1; 36560 36561 /* 36562 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD 36563 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as 36564 I'm aware. 36565 */ 36566 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 36567 /* NetBSD 8.0+ */ 36568 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || 36569 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { 36570 return MA_SHARE_MODE_NOT_SUPPORTED; 36571 } 36572 #else 36573 /* All other flavors. */ 36574 #endif 36575 36576 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 36577 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); 36578 if (result != MA_SUCCESS) { 36579 return result; 36580 } 36581 } 36582 36583 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 36584 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); 36585 if (result != MA_SUCCESS) { 36586 if (pConfig->deviceType == ma_device_type_duplex) { 36587 close(pDevice->audio4.fdCapture); 36588 } 36589 return result; 36590 } 36591 } 36592 36593 return MA_SUCCESS; 36594 } 36595 36596 static ma_result ma_device_start__audio4(ma_device* pDevice) 36597 { 36598 MA_ASSERT(pDevice != NULL); 36599 36600 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 36601 if (pDevice->audio4.fdCapture == -1) { 36602 return MA_INVALID_ARGS; 36603 } 36604 } 36605 36606 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 36607 if (pDevice->audio4.fdPlayback == -1) { 36608 return MA_INVALID_ARGS; 36609 } 36610 } 36611 36612 return MA_SUCCESS; 36613 } 36614 36615 static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) 36616 { 36617 if (fd == -1) { 36618 return MA_INVALID_ARGS; 36619 } 36620 36621 #if !defined(MA_AUDIO4_USE_NEW_API) 36622 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { 36623 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); 36624 return ma_result_from_errno(errno); 36625 } 36626 #else 36627 if (ioctl(fd, AUDIO_STOP, 0) < 0) { 36628 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); 36629 return ma_result_from_errno(errno); 36630 } 36631 #endif 36632 36633 return MA_SUCCESS; 36634 } 36635 36636 static ma_result ma_device_stop__audio4(ma_device* pDevice) 36637 { 36638 MA_ASSERT(pDevice != NULL); 36639 36640 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 36641 ma_result result; 36642 36643 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); 36644 if (result != MA_SUCCESS) { 36645 return result; 36646 } 36647 } 36648 36649 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 36650 ma_result result; 36651 36652 /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ 36653 #if !defined(MA_AUDIO4_USE_NEW_API) 36654 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); 36655 #endif 36656 36657 /* Here is where the device is stopped immediately. */ 36658 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); 36659 if (result != MA_SUCCESS) { 36660 return result; 36661 } 36662 } 36663 36664 return MA_SUCCESS; 36665 } 36666 36667 static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 36668 { 36669 int result; 36670 36671 if (pFramesWritten != NULL) { 36672 *pFramesWritten = 0; 36673 } 36674 36675 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 36676 if (result < 0) { 36677 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); 36678 return ma_result_from_errno(errno); 36679 } 36680 36681 if (pFramesWritten != NULL) { 36682 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 36683 } 36684 36685 return MA_SUCCESS; 36686 } 36687 36688 static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 36689 { 36690 int result; 36691 36692 if (pFramesRead != NULL) { 36693 *pFramesRead = 0; 36694 } 36695 36696 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 36697 if (result < 0) { 36698 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); 36699 return ma_result_from_errno(errno); 36700 } 36701 36702 if (pFramesRead != NULL) { 36703 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 36704 } 36705 36706 return MA_SUCCESS; 36707 } 36708 36709 static ma_result ma_context_uninit__audio4(ma_context* pContext) 36710 { 36711 MA_ASSERT(pContext != NULL); 36712 MA_ASSERT(pContext->backend == ma_backend_audio4); 36713 36714 (void)pContext; 36715 return MA_SUCCESS; 36716 } 36717 36718 static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 36719 { 36720 MA_ASSERT(pContext != NULL); 36721 36722 (void)pConfig; 36723 36724 pCallbacks->onContextInit = ma_context_init__audio4; 36725 pCallbacks->onContextUninit = ma_context_uninit__audio4; 36726 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; 36727 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; 36728 pCallbacks->onDeviceInit = ma_device_init__audio4; 36729 pCallbacks->onDeviceUninit = ma_device_uninit__audio4; 36730 pCallbacks->onDeviceStart = ma_device_start__audio4; 36731 pCallbacks->onDeviceStop = ma_device_stop__audio4; 36732 pCallbacks->onDeviceRead = ma_device_read__audio4; 36733 pCallbacks->onDeviceWrite = ma_device_write__audio4; 36734 pCallbacks->onDeviceDataLoop = NULL; 36735 36736 return MA_SUCCESS; 36737 } 36738 #endif /* audio4 */ 36739 36740 36741 /****************************************************************************** 36742 36743 OSS Backend 36744 36745 ******************************************************************************/ 36746 #ifdef MA_HAS_OSS 36747 #include <sys/ioctl.h> 36748 #include <unistd.h> 36749 #include <fcntl.h> 36750 #include <sys/soundcard.h> 36751 36752 #ifndef SNDCTL_DSP_HALT 36753 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET 36754 #endif 36755 36756 #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" 36757 36758 static int ma_open_temp_device__oss() 36759 { 36760 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ 36761 int fd = open("/dev/mixer", O_RDONLY, 0); 36762 if (fd >= 0) { 36763 return fd; 36764 } 36765 36766 return -1; 36767 } 36768 36769 static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) 36770 { 36771 const char* deviceName; 36772 int flags; 36773 36774 MA_ASSERT(pContext != NULL); 36775 MA_ASSERT(pfd != NULL); 36776 (void)pContext; 36777 36778 *pfd = -1; 36779 36780 /* This function should only be called for playback or capture, not duplex. */ 36781 if (deviceType == ma_device_type_duplex) { 36782 return MA_INVALID_ARGS; 36783 } 36784 36785 deviceName = MA_OSS_DEFAULT_DEVICE_NAME; 36786 if (pDeviceID != NULL) { 36787 deviceName = pDeviceID->oss; 36788 } 36789 36790 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; 36791 if (shareMode == ma_share_mode_exclusive) { 36792 flags |= O_EXCL; 36793 } 36794 36795 *pfd = open(deviceName, flags, 0); 36796 if (*pfd == -1) { 36797 return ma_result_from_errno(errno); 36798 } 36799 36800 return MA_SUCCESS; 36801 } 36802 36803 static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 36804 { 36805 int fd; 36806 oss_sysinfo si; 36807 int result; 36808 36809 MA_ASSERT(pContext != NULL); 36810 MA_ASSERT(callback != NULL); 36811 36812 fd = ma_open_temp_device__oss(); 36813 if (fd == -1) { 36814 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); 36815 return MA_NO_BACKEND; 36816 } 36817 36818 result = ioctl(fd, SNDCTL_SYSINFO, &si); 36819 if (result != -1) { 36820 int iAudioDevice; 36821 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { 36822 oss_audioinfo ai; 36823 ai.dev = iAudioDevice; 36824 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); 36825 if (result != -1) { 36826 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ 36827 ma_device_info deviceInfo; 36828 ma_bool32 isTerminating = MA_FALSE; 36829 36830 MA_ZERO_OBJECT(&deviceInfo); 36831 36832 /* ID */ 36833 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); 36834 36835 /* 36836 The human readable device name should be in the "ai.handle" variable, but it can 36837 sometimes be empty in which case we just fall back to "ai.name" which is less user 36838 friendly, but usually has a value. 36839 */ 36840 if (ai.handle[0] != '\0') { 36841 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); 36842 } else { 36843 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); 36844 } 36845 36846 /* The device can be both playback and capture. */ 36847 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { 36848 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 36849 } 36850 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { 36851 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 36852 } 36853 36854 if (isTerminating) { 36855 break; 36856 } 36857 } 36858 } 36859 } 36860 } else { 36861 close(fd); 36862 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); 36863 return MA_NO_BACKEND; 36864 } 36865 36866 close(fd); 36867 return MA_SUCCESS; 36868 } 36869 36870 static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) 36871 { 36872 unsigned int minChannels; 36873 unsigned int maxChannels; 36874 unsigned int iRate; 36875 36876 MA_ASSERT(pContext != NULL); 36877 MA_ASSERT(pAudioInfo != NULL); 36878 MA_ASSERT(pDeviceInfo != NULL); 36879 36880 /* If we support all channels we just report 0. */ 36881 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); 36882 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); 36883 36884 /* 36885 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, 36886 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which 36887 case we'll need to use min_rate and max_rate and report only standard rates. 36888 */ 36889 if (pAudioInfo->nrates > 0) { 36890 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { 36891 unsigned int rate = pAudioInfo->rates[iRate]; 36892 36893 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { 36894 ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ 36895 } else { 36896 unsigned int iChannel; 36897 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { 36898 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); 36899 } 36900 } 36901 } 36902 } else { 36903 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { 36904 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; 36905 36906 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { 36907 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { 36908 ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ 36909 } else { 36910 unsigned int iChannel; 36911 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { 36912 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); 36913 } 36914 } 36915 } 36916 } 36917 } 36918 } 36919 36920 static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 36921 { 36922 ma_bool32 foundDevice; 36923 int fdTemp; 36924 oss_sysinfo si; 36925 int result; 36926 36927 MA_ASSERT(pContext != NULL); 36928 36929 /* Handle the default device a little differently. */ 36930 if (pDeviceID == NULL) { 36931 if (deviceType == ma_device_type_playback) { 36932 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 36933 } else { 36934 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 36935 } 36936 36937 return MA_SUCCESS; 36938 } 36939 36940 36941 /* If we get here it means we are _not_ using the default device. */ 36942 foundDevice = MA_FALSE; 36943 36944 fdTemp = ma_open_temp_device__oss(); 36945 if (fdTemp == -1) { 36946 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); 36947 return MA_NO_BACKEND; 36948 } 36949 36950 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); 36951 if (result != -1) { 36952 int iAudioDevice; 36953 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { 36954 oss_audioinfo ai; 36955 ai.dev = iAudioDevice; 36956 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); 36957 if (result != -1) { 36958 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { 36959 /* It has the same name, so now just confirm the type. */ 36960 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || 36961 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { 36962 unsigned int formatMask; 36963 36964 /* ID */ 36965 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); 36966 36967 /* 36968 The human readable device name should be in the "ai.handle" variable, but it can 36969 sometimes be empty in which case we just fall back to "ai.name" which is less user 36970 friendly, but usually has a value. 36971 */ 36972 if (ai.handle[0] != '\0') { 36973 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); 36974 } else { 36975 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); 36976 } 36977 36978 36979 pDeviceInfo->nativeDataFormatCount = 0; 36980 36981 if (deviceType == ma_device_type_playback) { 36982 formatMask = ai.oformats; 36983 } else { 36984 formatMask = ai.iformats; 36985 } 36986 36987 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { 36988 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); 36989 } 36990 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { 36991 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); 36992 } 36993 if ((formatMask & AFMT_U8) != 0) { 36994 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); 36995 } 36996 36997 foundDevice = MA_TRUE; 36998 break; 36999 } 37000 } 37001 } 37002 } 37003 } else { 37004 close(fdTemp); 37005 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); 37006 return MA_NO_BACKEND; 37007 } 37008 37009 37010 close(fdTemp); 37011 37012 if (!foundDevice) { 37013 return MA_NO_DEVICE; 37014 } 37015 37016 return MA_SUCCESS; 37017 } 37018 37019 static ma_result ma_device_uninit__oss(ma_device* pDevice) 37020 { 37021 MA_ASSERT(pDevice != NULL); 37022 37023 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 37024 close(pDevice->oss.fdCapture); 37025 } 37026 37027 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 37028 close(pDevice->oss.fdPlayback); 37029 } 37030 37031 return MA_SUCCESS; 37032 } 37033 37034 static int ma_format_to_oss(ma_format format) 37035 { 37036 int ossFormat = AFMT_U8; 37037 switch (format) { 37038 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; 37039 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; 37040 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; 37041 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; 37042 case ma_format_u8: 37043 default: ossFormat = AFMT_U8; break; 37044 } 37045 37046 return ossFormat; 37047 } 37048 37049 static ma_format ma_format_from_oss(int ossFormat) 37050 { 37051 if (ossFormat == AFMT_U8) { 37052 return ma_format_u8; 37053 } else { 37054 if (ma_is_little_endian()) { 37055 switch (ossFormat) { 37056 case AFMT_S16_LE: return ma_format_s16; 37057 case AFMT_S32_LE: return ma_format_s32; 37058 default: return ma_format_unknown; 37059 } 37060 } else { 37061 switch (ossFormat) { 37062 case AFMT_S16_BE: return ma_format_s16; 37063 case AFMT_S32_BE: return ma_format_s32; 37064 default: return ma_format_unknown; 37065 } 37066 } 37067 } 37068 37069 return ma_format_unknown; 37070 } 37071 37072 static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) 37073 { 37074 ma_result result; 37075 int ossResult; 37076 int fd; 37077 const ma_device_id* pDeviceID = NULL; 37078 ma_share_mode shareMode; 37079 int ossFormat; 37080 int ossChannels; 37081 int ossSampleRate; 37082 int ossFragment; 37083 37084 MA_ASSERT(pDevice != NULL); 37085 MA_ASSERT(pConfig != NULL); 37086 MA_ASSERT(deviceType != ma_device_type_duplex); 37087 37088 pDeviceID = pDescriptor->pDeviceID; 37089 shareMode = pDescriptor->shareMode; 37090 ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ 37091 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; 37092 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; 37093 37094 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); 37095 if (result != MA_SUCCESS) { 37096 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); 37097 return result; 37098 } 37099 37100 /* 37101 The OSS documantation is very clear about the order we should be initializing the device's properties: 37102 1) Format 37103 2) Channels 37104 3) Sample rate. 37105 */ 37106 37107 /* Format. */ 37108 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); 37109 if (ossResult == -1) { 37110 close(fd); 37111 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); 37112 return ma_result_from_errno(errno); 37113 } 37114 37115 /* Channels. */ 37116 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); 37117 if (ossResult == -1) { 37118 close(fd); 37119 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); 37120 return ma_result_from_errno(errno); 37121 } 37122 37123 /* Sample Rate. */ 37124 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); 37125 if (ossResult == -1) { 37126 close(fd); 37127 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); 37128 return ma_result_from_errno(errno); 37129 } 37130 37131 /* 37132 Buffer. 37133 37134 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if 37135 it should be done before or after format/channels/rate. 37136 37137 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual 37138 value. 37139 */ 37140 { 37141 ma_uint32 periodSizeInFrames; 37142 ma_uint32 periodSizeInBytes; 37143 ma_uint32 ossFragmentSizePower; 37144 37145 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); 37146 37147 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); 37148 if (periodSizeInBytes < 16) { 37149 periodSizeInBytes = 16; 37150 } 37151 37152 ossFragmentSizePower = 4; 37153 periodSizeInBytes >>= 4; 37154 while (periodSizeInBytes >>= 1) { 37155 ossFragmentSizePower += 1; 37156 } 37157 37158 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); 37159 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); 37160 if (ossResult == -1) { 37161 close(fd); 37162 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); 37163 return ma_result_from_errno(errno); 37164 } 37165 } 37166 37167 /* Internal settings. */ 37168 if (deviceType == ma_device_type_capture) { 37169 pDevice->oss.fdCapture = fd; 37170 } else { 37171 pDevice->oss.fdPlayback = fd; 37172 } 37173 37174 pDescriptor->format = ma_format_from_oss(ossFormat); 37175 pDescriptor->channels = ossChannels; 37176 pDescriptor->sampleRate = ossSampleRate; 37177 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); 37178 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); 37179 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); 37180 37181 if (pDescriptor->format == ma_format_unknown) { 37182 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); 37183 return MA_FORMAT_NOT_SUPPORTED; 37184 } 37185 37186 return MA_SUCCESS; 37187 } 37188 37189 static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 37190 { 37191 MA_ASSERT(pDevice != NULL); 37192 MA_ASSERT(pConfig != NULL); 37193 37194 MA_ZERO_OBJECT(&pDevice->oss); 37195 37196 if (pConfig->deviceType == ma_device_type_loopback) { 37197 return MA_DEVICE_TYPE_NOT_SUPPORTED; 37198 } 37199 37200 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 37201 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); 37202 if (result != MA_SUCCESS) { 37203 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); 37204 return result; 37205 } 37206 } 37207 37208 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 37209 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); 37210 if (result != MA_SUCCESS) { 37211 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); 37212 return result; 37213 } 37214 } 37215 37216 return MA_SUCCESS; 37217 } 37218 37219 /* 37220 Note on Starting and Stopping 37221 ============================= 37222 In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when 37223 trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will 37224 fail. Instead what we need to do is just not write or read to and from the device when the 37225 device is not running. 37226 37227 As a result, both the start and stop functions for OSS are just empty stubs. The starting and 37228 stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check 37229 the device state, and if the device is stopped they will simply not do any kind of processing. 37230 37231 The downside to this technique is that I've noticed a fairly lengthy delay in stopping the 37232 device, up to a second. This is on a virtual machine, and as such might just be due to the 37233 virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for 37234 the moment that's just how it's going to have to be. 37235 37236 When starting the device, OSS will automatically start it when write() or read() is called. 37237 */ 37238 static ma_result ma_device_start__oss(ma_device* pDevice) 37239 { 37240 MA_ASSERT(pDevice != NULL); 37241 37242 /* The device is automatically started with reading and writing. */ 37243 (void)pDevice; 37244 37245 return MA_SUCCESS; 37246 } 37247 37248 static ma_result ma_device_stop__oss(ma_device* pDevice) 37249 { 37250 MA_ASSERT(pDevice != NULL); 37251 37252 /* See note above on why this is empty. */ 37253 (void)pDevice; 37254 37255 return MA_SUCCESS; 37256 } 37257 37258 static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) 37259 { 37260 int resultOSS; 37261 ma_uint32 deviceState; 37262 37263 if (pFramesWritten != NULL) { 37264 *pFramesWritten = 0; 37265 } 37266 37267 /* Don't do any processing if the device is stopped. */ 37268 deviceState = ma_device_get_state(pDevice); 37269 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { 37270 return MA_SUCCESS; 37271 } 37272 37273 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 37274 if (resultOSS < 0) { 37275 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); 37276 return ma_result_from_errno(errno); 37277 } 37278 37279 if (pFramesWritten != NULL) { 37280 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 37281 } 37282 37283 return MA_SUCCESS; 37284 } 37285 37286 static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) 37287 { 37288 int resultOSS; 37289 ma_uint32 deviceState; 37290 37291 if (pFramesRead != NULL) { 37292 *pFramesRead = 0; 37293 } 37294 37295 /* Don't do any processing if the device is stopped. */ 37296 deviceState = ma_device_get_state(pDevice); 37297 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { 37298 return MA_SUCCESS; 37299 } 37300 37301 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); 37302 if (resultOSS < 0) { 37303 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); 37304 return ma_result_from_errno(errno); 37305 } 37306 37307 if (pFramesRead != NULL) { 37308 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 37309 } 37310 37311 return MA_SUCCESS; 37312 } 37313 37314 static ma_result ma_context_uninit__oss(ma_context* pContext) 37315 { 37316 MA_ASSERT(pContext != NULL); 37317 MA_ASSERT(pContext->backend == ma_backend_oss); 37318 37319 (void)pContext; 37320 return MA_SUCCESS; 37321 } 37322 37323 static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 37324 { 37325 int fd; 37326 int ossVersion; 37327 int result; 37328 37329 MA_ASSERT(pContext != NULL); 37330 37331 (void)pConfig; 37332 37333 /* Try opening a temporary device first so we can get version information. This is closed at the end. */ 37334 fd = ma_open_temp_device__oss(); 37335 if (fd == -1) { 37336 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ 37337 return MA_NO_BACKEND; 37338 } 37339 37340 /* Grab the OSS version. */ 37341 ossVersion = 0; 37342 result = ioctl(fd, OSS_GETVERSION, &ossVersion); 37343 if (result == -1) { 37344 close(fd); 37345 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); 37346 return MA_NO_BACKEND; 37347 } 37348 37349 /* The file handle to temp device is no longer needed. Close ASAP. */ 37350 close(fd); 37351 37352 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); 37353 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); 37354 37355 pCallbacks->onContextInit = ma_context_init__oss; 37356 pCallbacks->onContextUninit = ma_context_uninit__oss; 37357 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; 37358 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; 37359 pCallbacks->onDeviceInit = ma_device_init__oss; 37360 pCallbacks->onDeviceUninit = ma_device_uninit__oss; 37361 pCallbacks->onDeviceStart = ma_device_start__oss; 37362 pCallbacks->onDeviceStop = ma_device_stop__oss; 37363 pCallbacks->onDeviceRead = ma_device_read__oss; 37364 pCallbacks->onDeviceWrite = ma_device_write__oss; 37365 pCallbacks->onDeviceDataLoop = NULL; 37366 37367 return MA_SUCCESS; 37368 } 37369 #endif /* OSS */ 37370 37371 37372 37373 37374 37375 /****************************************************************************** 37376 37377 AAudio Backend 37378 37379 ******************************************************************************/ 37380 #ifdef MA_HAS_AAUDIO 37381 37382 /*#include <AAudio/AAudio.h>*/ 37383 37384 typedef int32_t ma_aaudio_result_t; 37385 typedef int32_t ma_aaudio_direction_t; 37386 typedef int32_t ma_aaudio_sharing_mode_t; 37387 typedef int32_t ma_aaudio_format_t; 37388 typedef int32_t ma_aaudio_stream_state_t; 37389 typedef int32_t ma_aaudio_performance_mode_t; 37390 typedef int32_t ma_aaudio_usage_t; 37391 typedef int32_t ma_aaudio_content_type_t; 37392 typedef int32_t ma_aaudio_input_preset_t; 37393 typedef int32_t ma_aaudio_allowed_capture_policy_t; 37394 typedef int32_t ma_aaudio_data_callback_result_t; 37395 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; 37396 typedef struct ma_AAudioStream_t* ma_AAudioStream; 37397 37398 #define MA_AAUDIO_UNSPECIFIED 0 37399 37400 /* Result codes. miniaudio only cares about the success code. */ 37401 #define MA_AAUDIO_OK 0 37402 37403 /* Directions. */ 37404 #define MA_AAUDIO_DIRECTION_OUTPUT 0 37405 #define MA_AAUDIO_DIRECTION_INPUT 1 37406 37407 /* Sharing modes. */ 37408 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 37409 #define MA_AAUDIO_SHARING_MODE_SHARED 1 37410 37411 /* Formats. */ 37412 #define MA_AAUDIO_FORMAT_PCM_I16 1 37413 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2 37414 37415 /* Stream states. */ 37416 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 37417 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 37418 #define MA_AAUDIO_STREAM_STATE_OPEN 2 37419 #define MA_AAUDIO_STREAM_STATE_STARTING 3 37420 #define MA_AAUDIO_STREAM_STATE_STARTED 4 37421 #define MA_AAUDIO_STREAM_STATE_PAUSING 5 37422 #define MA_AAUDIO_STREAM_STATE_PAUSED 6 37423 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7 37424 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8 37425 #define MA_AAUDIO_STREAM_STATE_STOPPING 9 37426 #define MA_AAUDIO_STREAM_STATE_STOPPED 10 37427 #define MA_AAUDIO_STREAM_STATE_CLOSING 11 37428 #define MA_AAUDIO_STREAM_STATE_CLOSED 12 37429 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 37430 37431 /* Performance modes. */ 37432 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 37433 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 37434 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 37435 37436 /* Usage types. */ 37437 #define MA_AAUDIO_USAGE_MEDIA 1 37438 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 37439 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 37440 #define MA_AAUDIO_USAGE_ALARM 4 37441 #define MA_AAUDIO_USAGE_NOTIFICATION 5 37442 #define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 37443 #define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 37444 #define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 37445 #define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 37446 #define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 37447 #define MA_AAUDIO_USAGE_GAME 14 37448 #define MA_AAUDIO_USAGE_ASSISTANT 16 37449 #define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 37450 #define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 37451 #define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 37452 #define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 37453 37454 /* Content types. */ 37455 #define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 37456 #define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 37457 #define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 37458 #define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 37459 37460 /* Input presets. */ 37461 #define MA_AAUDIO_INPUT_PRESET_GENERIC 1 37462 #define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 37463 #define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 37464 #define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 37465 #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 37466 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 37467 37468 /* Allowed Capture Policies */ 37469 #define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 37470 #define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 37471 #define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 37472 37473 /* Callback results. */ 37474 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 37475 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1 37476 37477 37478 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); 37479 typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); 37480 37481 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); 37482 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); 37483 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); 37484 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); 37485 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); 37486 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); 37487 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); 37488 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); 37489 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); 37490 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); 37491 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); 37492 typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); 37493 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); 37494 typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); 37495 typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); 37496 typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); 37497 typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); 37498 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); 37499 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); 37500 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); 37501 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); 37502 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); 37503 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); 37504 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); 37505 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); 37506 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); 37507 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); 37508 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); 37509 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); 37510 37511 static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) 37512 { 37513 switch (resultAA) 37514 { 37515 case MA_AAUDIO_OK: return MA_SUCCESS; 37516 default: break; 37517 } 37518 37519 return MA_ERROR; 37520 } 37521 37522 static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) 37523 { 37524 switch (usage) { 37525 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; 37526 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; 37527 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; 37528 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; 37529 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; 37530 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; 37531 case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; 37532 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; 37533 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; 37534 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; 37535 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; 37536 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; 37537 case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; 37538 case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; 37539 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; 37540 case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; 37541 default: break; 37542 } 37543 37544 return MA_AAUDIO_USAGE_MEDIA; 37545 } 37546 37547 static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) 37548 { 37549 switch (contentType) { 37550 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; 37551 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; 37552 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; 37553 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; 37554 default: break; 37555 } 37556 37557 return MA_AAUDIO_CONTENT_TYPE_SPEECH; 37558 } 37559 37560 static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) 37561 { 37562 switch (inputPreset) { 37563 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; 37564 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; 37565 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; 37566 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; 37567 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; 37568 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; 37569 default: break; 37570 } 37571 37572 return MA_AAUDIO_INPUT_PRESET_GENERIC; 37573 } 37574 37575 static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) 37576 { 37577 switch (allowedCapturePolicy) { 37578 case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; 37579 case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; 37580 case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; 37581 default: break; 37582 } 37583 37584 return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; 37585 } 37586 37587 static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) 37588 { 37589 ma_result result; 37590 ma_job job; 37591 ma_device* pDevice = (ma_device*)pUserData; 37592 MA_ASSERT(pDevice != NULL); 37593 37594 (void)error; 37595 37596 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); 37597 37598 /* 37599 When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, 37600 we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this 37601 cleanly and safely. 37602 */ 37603 job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); 37604 job.data.device.aaudio.reroute.pDevice = pDevice; 37605 37606 if (pStream == pDevice->aaudio.pStreamCapture) { 37607 job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; 37608 } 37609 else { 37610 job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; 37611 } 37612 37613 result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); 37614 if (result != MA_SUCCESS) { 37615 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); 37616 return; 37617 } 37618 } 37619 37620 static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) 37621 { 37622 ma_device* pDevice = (ma_device*)pUserData; 37623 MA_ASSERT(pDevice != NULL); 37624 37625 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); 37626 37627 (void)pStream; 37628 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; 37629 } 37630 37631 static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) 37632 { 37633 ma_device* pDevice = (ma_device*)pUserData; 37634 MA_ASSERT(pDevice != NULL); 37635 37636 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); 37637 37638 (void)pStream; 37639 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; 37640 } 37641 37642 static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) 37643 { 37644 ma_AAudioStreamBuilder* pBuilder; 37645 ma_aaudio_result_t resultAA; 37646 37647 /* Safety. */ 37648 *ppBuilder = NULL; 37649 37650 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); 37651 if (resultAA != MA_AAUDIO_OK) { 37652 return ma_result_from_aaudio(resultAA); 37653 } 37654 37655 if (pDeviceID != NULL) { 37656 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); 37657 } 37658 37659 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); 37660 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); 37661 37662 37663 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ 37664 if (pDescriptor != NULL) { 37665 MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ 37666 37667 if (pDescriptor->sampleRate != 0) { 37668 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); 37669 } 37670 37671 if (deviceType == ma_device_type_capture) { 37672 if (pDescriptor->channels != 0) { 37673 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); 37674 } 37675 if (pDescriptor->format != ma_format_unknown) { 37676 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); 37677 } 37678 } else { 37679 if (pDescriptor->channels != 0) { 37680 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); 37681 } 37682 if (pDescriptor->format != ma_format_unknown) { 37683 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); 37684 } 37685 } 37686 37687 37688 /* 37689 There have been reports where setting the frames per data callback results in an error 37690 later on from Android. To address this, I'm experimenting with simply not setting it on 37691 anything from Android 11 and earlier. Suggestions welcome on how we might be able to make 37692 this more targetted. 37693 */ 37694 if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { 37695 /* 37696 AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you 37697 retrieve the actual sample rate until after you've opened the stream. But you need to configure 37698 the buffer capacity before you open the stream... :/ 37699 37700 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. 37701 */ 37702 ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; 37703 37704 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); 37705 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); 37706 } 37707 37708 if (deviceType == ma_device_type_capture) { 37709 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { 37710 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); 37711 } 37712 37713 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); 37714 } else { 37715 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { 37716 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); 37717 } 37718 37719 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { 37720 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); 37721 } 37722 37723 if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { 37724 ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); 37725 } 37726 37727 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); 37728 } 37729 37730 /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ 37731 ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); 37732 37733 /* We need to set an error callback to detect device changes. */ 37734 if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ 37735 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); 37736 } 37737 } 37738 37739 *ppBuilder = pBuilder; 37740 37741 return MA_SUCCESS; 37742 } 37743 37744 static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) 37745 { 37746 ma_result result; 37747 37748 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); 37749 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); 37750 37751 return result; 37752 } 37753 37754 static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) 37755 { 37756 ma_result result; 37757 ma_AAudioStreamBuilder* pBuilder; 37758 37759 *ppStream = NULL; 37760 37761 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); 37762 if (result != MA_SUCCESS) { 37763 return result; 37764 } 37765 37766 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); 37767 } 37768 37769 static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) 37770 { 37771 ma_result result; 37772 ma_AAudioStreamBuilder* pBuilder; 37773 37774 MA_ASSERT(pDevice != NULL); 37775 MA_ASSERT(pDescriptor != NULL); 37776 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ 37777 37778 *ppStream = NULL; 37779 37780 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); 37781 if (result != MA_SUCCESS) { 37782 return result; 37783 } 37784 37785 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); 37786 } 37787 37788 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) 37789 { 37790 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); 37791 } 37792 37793 static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) 37794 { 37795 /* The only way to know this is to try creating a stream. */ 37796 ma_AAudioStream* pStream; 37797 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); 37798 if (result != MA_SUCCESS) { 37799 return MA_FALSE; 37800 } 37801 37802 ma_close_stream__aaudio(pContext, pStream); 37803 return MA_TRUE; 37804 } 37805 37806 static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) 37807 { 37808 ma_aaudio_stream_state_t actualNewState; 37809 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ 37810 if (resultAA != MA_AAUDIO_OK) { 37811 return ma_result_from_aaudio(resultAA); 37812 } 37813 37814 if (newState != actualNewState) { 37815 return MA_ERROR; /* Failed to transition into the expected state. */ 37816 } 37817 37818 return MA_SUCCESS; 37819 } 37820 37821 37822 static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 37823 { 37824 ma_bool32 cbResult = MA_TRUE; 37825 37826 MA_ASSERT(pContext != NULL); 37827 MA_ASSERT(callback != NULL); 37828 37829 /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ 37830 37831 /* Playback. */ 37832 if (cbResult) { 37833 ma_device_info deviceInfo; 37834 MA_ZERO_OBJECT(&deviceInfo); 37835 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; 37836 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 37837 37838 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { 37839 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 37840 } 37841 } 37842 37843 /* Capture. */ 37844 if (cbResult) { 37845 ma_device_info deviceInfo; 37846 MA_ZERO_OBJECT(&deviceInfo); 37847 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; 37848 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 37849 37850 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { 37851 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 37852 } 37853 } 37854 37855 return MA_SUCCESS; 37856 } 37857 37858 static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) 37859 { 37860 MA_ASSERT(pContext != NULL); 37861 MA_ASSERT(pStream != NULL); 37862 MA_ASSERT(pDeviceInfo != NULL); 37863 37864 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; 37865 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); 37866 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); 37867 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; 37868 pDeviceInfo->nativeDataFormatCount += 1; 37869 } 37870 37871 static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) 37872 { 37873 /* AAudio supports s16 and f32. */ 37874 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); 37875 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); 37876 } 37877 37878 static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 37879 { 37880 ma_AAudioStream* pStream; 37881 ma_result result; 37882 37883 MA_ASSERT(pContext != NULL); 37884 37885 /* ID */ 37886 if (pDeviceID != NULL) { 37887 pDeviceInfo->id.aaudio = pDeviceID->aaudio; 37888 } else { 37889 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; 37890 } 37891 37892 /* Name */ 37893 if (deviceType == ma_device_type_playback) { 37894 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 37895 } else { 37896 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 37897 } 37898 37899 37900 pDeviceInfo->nativeDataFormatCount = 0; 37901 37902 /* We'll need to open the device to get accurate sample rate and channel count information. */ 37903 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); 37904 if (result != MA_SUCCESS) { 37905 return result; 37906 } 37907 37908 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); 37909 37910 ma_close_stream__aaudio(pContext, pStream); 37911 pStream = NULL; 37912 37913 return MA_SUCCESS; 37914 } 37915 37916 37917 static ma_result ma_device_uninit__aaudio(ma_device* pDevice) 37918 { 37919 MA_ASSERT(pDevice != NULL); 37920 37921 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 37922 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); 37923 pDevice->aaudio.pStreamCapture = NULL; 37924 } 37925 37926 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 37927 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); 37928 pDevice->aaudio.pStreamPlayback = NULL; 37929 } 37930 37931 return MA_SUCCESS; 37932 } 37933 37934 static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) 37935 { 37936 ma_result result; 37937 int32_t bufferCapacityInFrames; 37938 int32_t framesPerDataCallback; 37939 ma_AAudioStream* pStream; 37940 37941 MA_ASSERT(pDevice != NULL); 37942 MA_ASSERT(pConfig != NULL); 37943 MA_ASSERT(pDescriptor != NULL); 37944 37945 *ppStream = NULL; /* Safety. */ 37946 37947 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ 37948 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); 37949 if (result != MA_SUCCESS) { 37950 return result; /* Failed to open the AAudio stream. */ 37951 } 37952 37953 /* Now extract the internal configuration. */ 37954 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; 37955 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); 37956 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); 37957 37958 /* For the channel map we need to be sure we don't overflow any buffers. */ 37959 if (pDescriptor->channels <= MA_MAX_CHANNELS) { 37960 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ 37961 } else { 37962 ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ 37963 } 37964 37965 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); 37966 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); 37967 37968 if (framesPerDataCallback > 0) { 37969 pDescriptor->periodSizeInFrames = framesPerDataCallback; 37970 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; 37971 } else { 37972 pDescriptor->periodSizeInFrames = bufferCapacityInFrames; 37973 pDescriptor->periodCount = 1; 37974 } 37975 37976 *ppStream = pStream; 37977 37978 return MA_SUCCESS; 37979 } 37980 37981 static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 37982 { 37983 ma_result result; 37984 37985 MA_ASSERT(pDevice != NULL); 37986 37987 if (pConfig->deviceType == ma_device_type_loopback) { 37988 return MA_DEVICE_TYPE_NOT_SUPPORTED; 37989 } 37990 37991 pDevice->aaudio.usage = pConfig->aaudio.usage; 37992 pDevice->aaudio.contentType = pConfig->aaudio.contentType; 37993 pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; 37994 pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; 37995 pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; 37996 37997 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 37998 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); 37999 if (result != MA_SUCCESS) { 38000 return result; 38001 } 38002 } 38003 38004 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 38005 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); 38006 if (result != MA_SUCCESS) { 38007 return result; 38008 } 38009 } 38010 38011 return MA_SUCCESS; 38012 } 38013 38014 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) 38015 { 38016 ma_aaudio_result_t resultAA; 38017 ma_aaudio_stream_state_t currentState; 38018 38019 MA_ASSERT(pDevice != NULL); 38020 38021 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); 38022 if (resultAA != MA_AAUDIO_OK) { 38023 return ma_result_from_aaudio(resultAA); 38024 } 38025 38026 /* Do we actually need to wait for the device to transition into it's started state? */ 38027 38028 /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ 38029 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); 38030 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { 38031 ma_result result; 38032 38033 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { 38034 return MA_ERROR; /* Expecting the stream to be a starting or started state. */ 38035 } 38036 38037 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); 38038 if (result != MA_SUCCESS) { 38039 return result; 38040 } 38041 } 38042 38043 return MA_SUCCESS; 38044 } 38045 38046 static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) 38047 { 38048 ma_aaudio_result_t resultAA; 38049 ma_aaudio_stream_state_t currentState; 38050 38051 MA_ASSERT(pDevice != NULL); 38052 38053 /* 38054 From the AAudio documentation: 38055 38056 The stream will stop after all of the data currently buffered has been played. 38057 38058 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. 38059 */ 38060 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); 38061 if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { 38062 return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ 38063 } 38064 38065 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); 38066 if (resultAA != MA_AAUDIO_OK) { 38067 return ma_result_from_aaudio(resultAA); 38068 } 38069 38070 /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ 38071 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); 38072 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { 38073 ma_result result; 38074 38075 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { 38076 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ 38077 } 38078 38079 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); 38080 if (result != MA_SUCCESS) { 38081 return result; 38082 } 38083 } 38084 38085 return MA_SUCCESS; 38086 } 38087 38088 static ma_result ma_device_start__aaudio(ma_device* pDevice) 38089 { 38090 MA_ASSERT(pDevice != NULL); 38091 38092 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 38093 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); 38094 if (result != MA_SUCCESS) { 38095 return result; 38096 } 38097 } 38098 38099 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 38100 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); 38101 if (result != MA_SUCCESS) { 38102 if (pDevice->type == ma_device_type_duplex) { 38103 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); 38104 } 38105 return result; 38106 } 38107 } 38108 38109 return MA_SUCCESS; 38110 } 38111 38112 static ma_result ma_device_stop__aaudio(ma_device* pDevice) 38113 { 38114 MA_ASSERT(pDevice != NULL); 38115 38116 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 38117 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); 38118 if (result != MA_SUCCESS) { 38119 return result; 38120 } 38121 } 38122 38123 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 38124 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); 38125 if (result != MA_SUCCESS) { 38126 return result; 38127 } 38128 } 38129 38130 ma_device__on_notification_stopped(pDevice); 38131 38132 return MA_SUCCESS; 38133 } 38134 38135 static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) 38136 { 38137 ma_result result; 38138 38139 MA_ASSERT(pDevice != NULL); 38140 38141 /* The first thing to do is close the streams. */ 38142 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { 38143 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); 38144 pDevice->aaudio.pStreamCapture = NULL; 38145 } 38146 38147 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 38148 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); 38149 pDevice->aaudio.pStreamPlayback = NULL; 38150 } 38151 38152 /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ 38153 { 38154 ma_device_config deviceConfig; 38155 ma_device_descriptor descriptorPlayback; 38156 ma_device_descriptor descriptorCapture; 38157 38158 deviceConfig = ma_device_config_init(deviceType); 38159 deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ 38160 deviceConfig.playback.shareMode = pDevice->playback.shareMode; 38161 deviceConfig.playback.format = pDevice->playback.format; 38162 deviceConfig.playback.channels = pDevice->playback.channels; 38163 deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ 38164 deviceConfig.capture.shareMode = pDevice->capture.shareMode; 38165 deviceConfig.capture.format = pDevice->capture.format; 38166 deviceConfig.capture.channels = pDevice->capture.channels; 38167 deviceConfig.sampleRate = pDevice->sampleRate; 38168 deviceConfig.aaudio.usage = pDevice->aaudio.usage; 38169 deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; 38170 deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; 38171 deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; 38172 deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; 38173 deviceConfig.periods = 1; 38174 38175 /* Try to get an accurate period size. */ 38176 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 38177 deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; 38178 } else { 38179 deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; 38180 } 38181 38182 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 38183 descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; 38184 descriptorCapture.shareMode = deviceConfig.capture.shareMode; 38185 descriptorCapture.format = deviceConfig.capture.format; 38186 descriptorCapture.channels = deviceConfig.capture.channels; 38187 descriptorCapture.sampleRate = deviceConfig.sampleRate; 38188 descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; 38189 descriptorCapture.periodCount = deviceConfig.periods; 38190 } 38191 38192 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 38193 descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; 38194 descriptorPlayback.shareMode = deviceConfig.playback.shareMode; 38195 descriptorPlayback.format = deviceConfig.playback.format; 38196 descriptorPlayback.channels = deviceConfig.playback.channels; 38197 descriptorPlayback.sampleRate = deviceConfig.sampleRate; 38198 descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; 38199 descriptorPlayback.periodCount = deviceConfig.periods; 38200 } 38201 38202 result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); 38203 if (result != MA_SUCCESS) { 38204 return result; 38205 } 38206 38207 result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); 38208 if (result != MA_SUCCESS) { 38209 ma_device_uninit__aaudio(pDevice); 38210 return result; 38211 } 38212 38213 /* We'll only ever do this in response to a reroute. */ 38214 ma_device__on_notification_rerouted(pDevice); 38215 38216 /* If the device is started, start the streams. Maybe make this configurable? */ 38217 if (ma_device_get_state(pDevice) == ma_device_state_started) { 38218 if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { 38219 ma_device_start__aaudio(pDevice); 38220 } else { 38221 ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ 38222 } 38223 } 38224 38225 return MA_SUCCESS; 38226 } 38227 } 38228 38229 static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) 38230 { 38231 ma_AAudioStream* pStream = NULL; 38232 38233 MA_ASSERT(pDevice != NULL); 38234 MA_ASSERT(type != ma_device_type_duplex); 38235 MA_ASSERT(pDeviceInfo != NULL); 38236 38237 if (type == ma_device_type_playback) { 38238 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; 38239 pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; 38240 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ 38241 } 38242 if (type == ma_device_type_capture) { 38243 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; 38244 pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; 38245 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ 38246 } 38247 38248 /* Safety. Should never happen. */ 38249 if (pStream == NULL) { 38250 return MA_INVALID_OPERATION; 38251 } 38252 38253 pDeviceInfo->nativeDataFormatCount = 0; 38254 ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); 38255 38256 return MA_SUCCESS; 38257 } 38258 38259 38260 static ma_result ma_context_uninit__aaudio(ma_context* pContext) 38261 { 38262 MA_ASSERT(pContext != NULL); 38263 MA_ASSERT(pContext->backend == ma_backend_aaudio); 38264 38265 ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); 38266 38267 ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); 38268 pContext->aaudio.hAAudio = NULL; 38269 38270 return MA_SUCCESS; 38271 } 38272 38273 static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 38274 { 38275 size_t i; 38276 const char* libNames[] = { 38277 "libaaudio.so" 38278 }; 38279 38280 for (i = 0; i < ma_countof(libNames); ++i) { 38281 pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); 38282 if (pContext->aaudio.hAAudio != NULL) { 38283 break; 38284 } 38285 } 38286 38287 if (pContext->aaudio.hAAudio == NULL) { 38288 return MA_FAILED_TO_INIT_BACKEND; 38289 } 38290 38291 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); 38292 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); 38293 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); 38294 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); 38295 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); 38296 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); 38297 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); 38298 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); 38299 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); 38300 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); 38301 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); 38302 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); 38303 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); 38304 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); 38305 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); 38306 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); 38307 pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); 38308 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); 38309 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); 38310 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); 38311 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); 38312 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); 38313 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); 38314 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); 38315 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); 38316 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); 38317 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); 38318 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); 38319 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); 38320 38321 38322 pCallbacks->onContextInit = ma_context_init__aaudio; 38323 pCallbacks->onContextUninit = ma_context_uninit__aaudio; 38324 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; 38325 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; 38326 pCallbacks->onDeviceInit = ma_device_init__aaudio; 38327 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; 38328 pCallbacks->onDeviceStart = ma_device_start__aaudio; 38329 pCallbacks->onDeviceStop = ma_device_stop__aaudio; 38330 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ 38331 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ 38332 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ 38333 pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; 38334 38335 38336 /* We need a job thread so we can deal with rerouting. */ 38337 { 38338 ma_result result; 38339 ma_device_job_thread_config jobThreadConfig; 38340 38341 jobThreadConfig = ma_device_job_thread_config_init(); 38342 38343 result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); 38344 if (result != MA_SUCCESS) { 38345 ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); 38346 pContext->aaudio.hAAudio = NULL; 38347 return result; 38348 } 38349 } 38350 38351 38352 (void)pConfig; 38353 return MA_SUCCESS; 38354 } 38355 38356 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) 38357 { 38358 ma_device* pDevice; 38359 38360 MA_ASSERT(pJob != NULL); 38361 38362 pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; 38363 MA_ASSERT(pDevice != NULL); 38364 38365 /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ 38366 return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); 38367 } 38368 #else 38369 /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ 38370 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) 38371 { 38372 return ma_job_process__noop(pJob); 38373 } 38374 #endif /* AAudio */ 38375 38376 38377 /****************************************************************************** 38378 38379 OpenSL|ES Backend 38380 38381 ******************************************************************************/ 38382 #ifdef MA_HAS_OPENSL 38383 #include <SLES/OpenSLES.h> 38384 #ifdef MA_ANDROID 38385 #include <SLES/OpenSLES_Android.h> 38386 #endif 38387 38388 typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); 38389 38390 /* OpenSL|ES has one-per-application objects :( */ 38391 static SLObjectItf g_maEngineObjectSL = NULL; 38392 static SLEngineItf g_maEngineSL = NULL; 38393 static ma_uint32 g_maOpenSLInitCounter = 0; 38394 static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ 38395 38396 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) 38397 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) 38398 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) 38399 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) 38400 38401 #ifdef MA_ANDROID 38402 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) 38403 #else 38404 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) 38405 #endif 38406 38407 static ma_result ma_result_from_OpenSL(SLuint32 result) 38408 { 38409 switch (result) 38410 { 38411 case SL_RESULT_SUCCESS: return MA_SUCCESS; 38412 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; 38413 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; 38414 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; 38415 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; 38416 case SL_RESULT_RESOURCE_LOST: return MA_ERROR; 38417 case SL_RESULT_IO_ERROR: return MA_IO_ERROR; 38418 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; 38419 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; 38420 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; 38421 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; 38422 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; 38423 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; 38424 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; 38425 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; 38426 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; 38427 case SL_RESULT_CONTROL_LOST: return MA_ERROR; 38428 default: return MA_ERROR; 38429 } 38430 } 38431 38432 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ 38433 static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) 38434 { 38435 switch (id) 38436 { 38437 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; 38438 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; 38439 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; 38440 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; 38441 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; 38442 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; 38443 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; 38444 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; 38445 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; 38446 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; 38447 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; 38448 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; 38449 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; 38450 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; 38451 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; 38452 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; 38453 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; 38454 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; 38455 default: return 0; 38456 } 38457 } 38458 38459 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ 38460 static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) 38461 { 38462 switch (id) 38463 { 38464 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; 38465 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; 38466 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; 38467 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; 38468 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; 38469 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; 38470 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; 38471 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; 38472 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; 38473 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; 38474 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; 38475 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; 38476 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; 38477 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; 38478 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; 38479 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; 38480 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; 38481 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; 38482 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; 38483 default: return 0; 38484 } 38485 } 38486 38487 /* Converts a channel mapping to an OpenSL-style channel mask. */ 38488 static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) 38489 { 38490 SLuint32 channelMask = 0; 38491 ma_uint32 iChannel; 38492 for (iChannel = 0; iChannel < channels; ++iChannel) { 38493 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); 38494 } 38495 38496 return channelMask; 38497 } 38498 38499 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */ 38500 static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) 38501 { 38502 if (channels == 1 && channelMask == 0) { 38503 pChannelMap[0] = MA_CHANNEL_MONO; 38504 } else if (channels == 2 && channelMask == 0) { 38505 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; 38506 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; 38507 } else { 38508 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { 38509 pChannelMap[0] = MA_CHANNEL_MONO; 38510 } else { 38511 /* Just iterate over each bit. */ 38512 ma_uint32 iChannel = 0; 38513 ma_uint32 iBit; 38514 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { 38515 SLuint32 bitValue = (channelMask & (1UL << iBit)); 38516 if (bitValue != 0) { 38517 /* The bit is set. */ 38518 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); 38519 iChannel += 1; 38520 } 38521 } 38522 } 38523 } 38524 } 38525 38526 static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) 38527 { 38528 if (samplesPerSec <= SL_SAMPLINGRATE_8) { 38529 return SL_SAMPLINGRATE_8; 38530 } 38531 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { 38532 return SL_SAMPLINGRATE_11_025; 38533 } 38534 if (samplesPerSec <= SL_SAMPLINGRATE_12) { 38535 return SL_SAMPLINGRATE_12; 38536 } 38537 if (samplesPerSec <= SL_SAMPLINGRATE_16) { 38538 return SL_SAMPLINGRATE_16; 38539 } 38540 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { 38541 return SL_SAMPLINGRATE_22_05; 38542 } 38543 if (samplesPerSec <= SL_SAMPLINGRATE_24) { 38544 return SL_SAMPLINGRATE_24; 38545 } 38546 if (samplesPerSec <= SL_SAMPLINGRATE_32) { 38547 return SL_SAMPLINGRATE_32; 38548 } 38549 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { 38550 return SL_SAMPLINGRATE_44_1; 38551 } 38552 if (samplesPerSec <= SL_SAMPLINGRATE_48) { 38553 return SL_SAMPLINGRATE_48; 38554 } 38555 38556 /* Android doesn't support more than 48000. */ 38557 #ifndef MA_ANDROID 38558 if (samplesPerSec <= SL_SAMPLINGRATE_64) { 38559 return SL_SAMPLINGRATE_64; 38560 } 38561 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { 38562 return SL_SAMPLINGRATE_88_2; 38563 } 38564 if (samplesPerSec <= SL_SAMPLINGRATE_96) { 38565 return SL_SAMPLINGRATE_96; 38566 } 38567 if (samplesPerSec <= SL_SAMPLINGRATE_192) { 38568 return SL_SAMPLINGRATE_192; 38569 } 38570 #endif 38571 38572 return SL_SAMPLINGRATE_16; 38573 } 38574 38575 38576 static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) 38577 { 38578 switch (streamType) { 38579 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; 38580 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; 38581 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; 38582 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; 38583 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; 38584 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; 38585 default: break; 38586 } 38587 38588 return SL_ANDROID_STREAM_VOICE; 38589 } 38590 38591 static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) 38592 { 38593 switch (recordingPreset) { 38594 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; 38595 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; 38596 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; 38597 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; 38598 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; 38599 default: break; 38600 } 38601 38602 return SL_ANDROID_RECORDING_PRESET_NONE; 38603 } 38604 38605 38606 static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 38607 { 38608 ma_bool32 cbResult; 38609 38610 MA_ASSERT(pContext != NULL); 38611 MA_ASSERT(callback != NULL); 38612 38613 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ 38614 if (g_maOpenSLInitCounter == 0) { 38615 return MA_INVALID_OPERATION; 38616 } 38617 38618 /* 38619 TODO: Test Me. 38620 38621 This is currently untested, so for now we are just returning default devices. 38622 */ 38623 #if 0 && !defined(MA_ANDROID) 38624 ma_bool32 isTerminated = MA_FALSE; 38625 38626 SLuint32 pDeviceIDs[128]; 38627 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); 38628 38629 SLAudioIODeviceCapabilitiesItf deviceCaps; 38630 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); 38631 if (resultSL != SL_RESULT_SUCCESS) { 38632 /* The interface may not be supported so just report a default device. */ 38633 goto return_default_device; 38634 } 38635 38636 /* Playback */ 38637 if (!isTerminated) { 38638 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); 38639 if (resultSL != SL_RESULT_SUCCESS) { 38640 return ma_result_from_OpenSL(resultSL); 38641 } 38642 38643 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { 38644 ma_device_info deviceInfo; 38645 MA_ZERO_OBJECT(&deviceInfo); 38646 deviceInfo.id.opensl = pDeviceIDs[iDevice]; 38647 38648 SLAudioOutputDescriptor desc; 38649 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); 38650 if (resultSL == SL_RESULT_SUCCESS) { 38651 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); 38652 38653 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 38654 if (cbResult == MA_FALSE) { 38655 isTerminated = MA_TRUE; 38656 break; 38657 } 38658 } 38659 } 38660 } 38661 38662 /* Capture */ 38663 if (!isTerminated) { 38664 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); 38665 if (resultSL != SL_RESULT_SUCCESS) { 38666 return ma_result_from_OpenSL(resultSL); 38667 } 38668 38669 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { 38670 ma_device_info deviceInfo; 38671 MA_ZERO_OBJECT(&deviceInfo); 38672 deviceInfo.id.opensl = pDeviceIDs[iDevice]; 38673 38674 SLAudioInputDescriptor desc; 38675 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); 38676 if (resultSL == SL_RESULT_SUCCESS) { 38677 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); 38678 38679 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 38680 if (cbResult == MA_FALSE) { 38681 isTerminated = MA_TRUE; 38682 break; 38683 } 38684 } 38685 } 38686 } 38687 38688 return MA_SUCCESS; 38689 #else 38690 goto return_default_device; 38691 #endif 38692 38693 return_default_device:; 38694 cbResult = MA_TRUE; 38695 38696 /* Playback. */ 38697 if (cbResult) { 38698 ma_device_info deviceInfo; 38699 MA_ZERO_OBJECT(&deviceInfo); 38700 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; 38701 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 38702 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 38703 } 38704 38705 /* Capture. */ 38706 if (cbResult) { 38707 ma_device_info deviceInfo; 38708 MA_ZERO_OBJECT(&deviceInfo); 38709 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; 38710 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 38711 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 38712 } 38713 38714 return MA_SUCCESS; 38715 } 38716 38717 static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) 38718 { 38719 MA_ASSERT(pContext != NULL); 38720 MA_ASSERT(pDeviceInfo != NULL); 38721 38722 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; 38723 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; 38724 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; 38725 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; 38726 pDeviceInfo->nativeDataFormatCount += 1; 38727 } 38728 38729 static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) 38730 { 38731 ma_uint32 minChannels = 1; 38732 ma_uint32 maxChannels = 2; 38733 ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; 38734 ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; 38735 ma_uint32 iChannel; 38736 ma_uint32 iSampleRate; 38737 38738 MA_ASSERT(pContext != NULL); 38739 MA_ASSERT(pDeviceInfo != NULL); 38740 38741 /* 38742 Each sample format can support mono and stereo, and we'll support a small subset of standard 38743 rates (up to 48000). A better solution would be to somehow find a native sample rate. 38744 */ 38745 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { 38746 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { 38747 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; 38748 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { 38749 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); 38750 } 38751 } 38752 } 38753 } 38754 38755 static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 38756 { 38757 MA_ASSERT(pContext != NULL); 38758 38759 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ 38760 if (g_maOpenSLInitCounter == 0) { 38761 return MA_INVALID_OPERATION; 38762 } 38763 38764 /* 38765 TODO: Test Me. 38766 38767 This is currently untested, so for now we are just returning default devices. 38768 */ 38769 #if 0 && !defined(MA_ANDROID) 38770 SLAudioIODeviceCapabilitiesItf deviceCaps; 38771 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); 38772 if (resultSL != SL_RESULT_SUCCESS) { 38773 /* The interface may not be supported so just report a default device. */ 38774 goto return_default_device; 38775 } 38776 38777 if (deviceType == ma_device_type_playback) { 38778 SLAudioOutputDescriptor desc; 38779 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); 38780 if (resultSL != SL_RESULT_SUCCESS) { 38781 return ma_result_from_OpenSL(resultSL); 38782 } 38783 38784 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); 38785 } else { 38786 SLAudioInputDescriptor desc; 38787 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); 38788 if (resultSL != SL_RESULT_SUCCESS) { 38789 return ma_result_from_OpenSL(resultSL); 38790 } 38791 38792 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); 38793 } 38794 38795 goto return_detailed_info; 38796 #else 38797 goto return_default_device; 38798 #endif 38799 38800 return_default_device: 38801 if (pDeviceID != NULL) { 38802 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || 38803 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { 38804 return MA_NO_DEVICE; /* Don't know the device. */ 38805 } 38806 } 38807 38808 /* ID and Name / Description */ 38809 if (deviceType == ma_device_type_playback) { 38810 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; 38811 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 38812 } else { 38813 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; 38814 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 38815 } 38816 38817 pDeviceInfo->isDefault = MA_TRUE; 38818 38819 goto return_detailed_info; 38820 38821 38822 return_detailed_info: 38823 38824 /* 38825 For now we're just outputting a set of values that are supported by the API but not necessarily supported 38826 by the device natively. Later on we should work on this so that it more closely reflects the device's 38827 actual native format. 38828 */ 38829 pDeviceInfo->nativeDataFormatCount = 0; 38830 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 38831 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); 38832 #endif 38833 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); 38834 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); 38835 38836 return MA_SUCCESS; 38837 } 38838 38839 38840 #ifdef MA_ANDROID 38841 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ 38842 static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) 38843 { 38844 ma_device* pDevice = (ma_device*)pUserData; 38845 size_t periodSizeInBytes; 38846 ma_uint8* pBuffer; 38847 SLresult resultSL; 38848 38849 MA_ASSERT(pDevice != NULL); 38850 38851 (void)pBufferQueue; 38852 38853 /* 38854 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like 38855 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, 38856 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( 38857 */ 38858 38859 /* Don't do anything if the device is not started. */ 38860 if (ma_device_get_state(pDevice) != ma_device_state_started) { 38861 return; 38862 } 38863 38864 /* Don't do anything if the device is being drained. */ 38865 if (pDevice->opensl.isDrainingCapture) { 38866 return; 38867 } 38868 38869 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 38870 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); 38871 38872 ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); 38873 38874 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); 38875 if (resultSL != SL_RESULT_SUCCESS) { 38876 return; 38877 } 38878 38879 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; 38880 } 38881 38882 static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) 38883 { 38884 ma_device* pDevice = (ma_device*)pUserData; 38885 size_t periodSizeInBytes; 38886 ma_uint8* pBuffer; 38887 SLresult resultSL; 38888 38889 MA_ASSERT(pDevice != NULL); 38890 38891 (void)pBufferQueue; 38892 38893 /* Don't do anything if the device is not started. */ 38894 if (ma_device_get_state(pDevice) != ma_device_state_started) { 38895 return; 38896 } 38897 38898 /* Don't do anything if the device is being drained. */ 38899 if (pDevice->opensl.isDrainingPlayback) { 38900 return; 38901 } 38902 38903 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 38904 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); 38905 38906 ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); 38907 38908 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); 38909 if (resultSL != SL_RESULT_SUCCESS) { 38910 return; 38911 } 38912 38913 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; 38914 } 38915 #endif 38916 38917 static ma_result ma_device_uninit__opensl(ma_device* pDevice) 38918 { 38919 MA_ASSERT(pDevice != NULL); 38920 38921 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ 38922 if (g_maOpenSLInitCounter == 0) { 38923 return MA_INVALID_OPERATION; 38924 } 38925 38926 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 38927 if (pDevice->opensl.pAudioRecorderObj) { 38928 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); 38929 } 38930 38931 ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); 38932 } 38933 38934 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 38935 if (pDevice->opensl.pAudioPlayerObj) { 38936 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); 38937 } 38938 if (pDevice->opensl.pOutputMixObj) { 38939 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); 38940 } 38941 38942 ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); 38943 } 38944 38945 return MA_SUCCESS; 38946 } 38947 38948 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 38949 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; 38950 #else 38951 typedef SLDataFormat_PCM ma_SLDataFormat_PCM; 38952 #endif 38953 38954 static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) 38955 { 38956 /* We need to convert our format/channels/rate so that they aren't set to default. */ 38957 if (format == ma_format_unknown) { 38958 format = MA_DEFAULT_FORMAT; 38959 } 38960 if (channels == 0) { 38961 channels = MA_DEFAULT_CHANNELS; 38962 } 38963 if (sampleRate == 0) { 38964 sampleRate = MA_DEFAULT_SAMPLE_RATE; 38965 } 38966 38967 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 38968 if (format == ma_format_f32) { 38969 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; 38970 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; 38971 } else { 38972 pDataFormat->formatType = SL_DATAFORMAT_PCM; 38973 } 38974 #else 38975 pDataFormat->formatType = SL_DATAFORMAT_PCM; 38976 #endif 38977 38978 pDataFormat->numChannels = channels; 38979 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ 38980 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; 38981 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); 38982 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; 38983 38984 /* 38985 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html 38986 - Only mono and stereo is supported. 38987 - Only u8 and s16 formats are supported. 38988 - Maximum sample rate of 48000. 38989 */ 38990 #ifdef MA_ANDROID 38991 if (pDataFormat->numChannels > 2) { 38992 pDataFormat->numChannels = 2; 38993 } 38994 #if __ANDROID_API__ >= 21 38995 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { 38996 /* It's floating point. */ 38997 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); 38998 if (pDataFormat->bitsPerSample > 32) { 38999 pDataFormat->bitsPerSample = 32; 39000 } 39001 } else { 39002 if (pDataFormat->bitsPerSample > 16) { 39003 pDataFormat->bitsPerSample = 16; 39004 } 39005 } 39006 #else 39007 if (pDataFormat->bitsPerSample > 16) { 39008 pDataFormat->bitsPerSample = 16; 39009 } 39010 #endif 39011 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { 39012 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; 39013 } 39014 #endif 39015 39016 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ 39017 39018 return MA_SUCCESS; 39019 } 39020 39021 static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 39022 { 39023 ma_bool32 isFloatingPoint = MA_FALSE; 39024 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 39025 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { 39026 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); 39027 isFloatingPoint = MA_TRUE; 39028 } 39029 #endif 39030 if (isFloatingPoint) { 39031 if (pDataFormat->bitsPerSample == 32) { 39032 *pFormat = ma_format_f32; 39033 } 39034 } else { 39035 if (pDataFormat->bitsPerSample == 8) { 39036 *pFormat = ma_format_u8; 39037 } else if (pDataFormat->bitsPerSample == 16) { 39038 *pFormat = ma_format_s16; 39039 } else if (pDataFormat->bitsPerSample == 24) { 39040 *pFormat = ma_format_s24; 39041 } else if (pDataFormat->bitsPerSample == 32) { 39042 *pFormat = ma_format_s32; 39043 } 39044 } 39045 39046 *pChannels = pDataFormat->numChannels; 39047 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; 39048 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); 39049 39050 return MA_SUCCESS; 39051 } 39052 39053 static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 39054 { 39055 #ifdef MA_ANDROID 39056 SLDataLocator_AndroidSimpleBufferQueue queue; 39057 SLresult resultSL; 39058 size_t bufferSizeInBytes; 39059 SLInterfaceID itfIDs[2]; 39060 const SLboolean itfIDsRequired[] = { 39061 SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ 39062 SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ 39063 }; 39064 #endif 39065 39066 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ 39067 if (g_maOpenSLInitCounter == 0) { 39068 return MA_INVALID_OPERATION; 39069 } 39070 39071 if (pConfig->deviceType == ma_device_type_loopback) { 39072 return MA_DEVICE_TYPE_NOT_SUPPORTED; 39073 } 39074 39075 /* 39076 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've 39077 been able to test with and I currently depend on Android-specific extensions (simple buffer 39078 queues). 39079 */ 39080 #ifdef MA_ANDROID 39081 itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; 39082 itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; 39083 39084 /* No exclusive mode with OpenSL|ES. */ 39085 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || 39086 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { 39087 return MA_SHARE_MODE_NOT_SUPPORTED; 39088 } 39089 39090 /* Now we can start initializing the device properly. */ 39091 MA_ASSERT(pDevice != NULL); 39092 MA_ZERO_OBJECT(&pDevice->opensl); 39093 39094 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; 39095 39096 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 39097 ma_SLDataFormat_PCM pcm; 39098 SLDataLocator_IODevice locatorDevice; 39099 SLDataSource source; 39100 SLDataSink sink; 39101 SLAndroidConfigurationItf pRecorderConfig; 39102 39103 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); 39104 39105 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; 39106 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; 39107 locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ 39108 locatorDevice.device = NULL; 39109 39110 source.pLocator = &locatorDevice; 39111 source.pFormat = NULL; 39112 39113 queue.numBuffers = pDescriptorCapture->periodCount; 39114 39115 sink.pLocator = &queue; 39116 sink.pFormat = (SLDataFormat_PCM*)&pcm; 39117 39118 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); 39119 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { 39120 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ 39121 pcm.formatType = SL_DATAFORMAT_PCM; 39122 pcm.numChannels = 1; 39123 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ 39124 pcm.bitsPerSample = 16; 39125 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ 39126 pcm.channelMask = 0; 39127 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); 39128 } 39129 39130 if (resultSL != SL_RESULT_SUCCESS) { 39131 ma_device_uninit__opensl(pDevice); 39132 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); 39133 return ma_result_from_OpenSL(resultSL); 39134 } 39135 39136 39137 /* Set the recording preset before realizing the player. */ 39138 if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { 39139 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); 39140 if (resultSL == SL_RESULT_SUCCESS) { 39141 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); 39142 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); 39143 if (resultSL != SL_RESULT_SUCCESS) { 39144 /* Failed to set the configuration. Just keep going. */ 39145 } 39146 } 39147 } 39148 39149 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); 39150 if (resultSL != SL_RESULT_SUCCESS) { 39151 ma_device_uninit__opensl(pDevice); 39152 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); 39153 return ma_result_from_OpenSL(resultSL); 39154 } 39155 39156 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); 39157 if (resultSL != SL_RESULT_SUCCESS) { 39158 ma_device_uninit__opensl(pDevice); 39159 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); 39160 return ma_result_from_OpenSL(resultSL); 39161 } 39162 39163 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); 39164 if (resultSL != SL_RESULT_SUCCESS) { 39165 ma_device_uninit__opensl(pDevice); 39166 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); 39167 return ma_result_from_OpenSL(resultSL); 39168 } 39169 39170 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); 39171 if (resultSL != SL_RESULT_SUCCESS) { 39172 ma_device_uninit__opensl(pDevice); 39173 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); 39174 return ma_result_from_OpenSL(resultSL); 39175 } 39176 39177 /* The internal format is determined by the "pcm" object. */ 39178 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); 39179 39180 /* Buffer. */ 39181 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); 39182 pDevice->opensl.currentBufferIndexCapture = 0; 39183 39184 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; 39185 pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); 39186 if (pDevice->opensl.pBufferCapture == NULL) { 39187 ma_device_uninit__opensl(pDevice); 39188 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); 39189 return MA_OUT_OF_MEMORY; 39190 } 39191 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); 39192 } 39193 39194 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 39195 ma_SLDataFormat_PCM pcm; 39196 SLDataSource source; 39197 SLDataLocator_OutputMix outmixLocator; 39198 SLDataSink sink; 39199 SLAndroidConfigurationItf pPlayerConfig; 39200 39201 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); 39202 39203 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); 39204 if (resultSL != SL_RESULT_SUCCESS) { 39205 ma_device_uninit__opensl(pDevice); 39206 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); 39207 return ma_result_from_OpenSL(resultSL); 39208 } 39209 39210 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); 39211 if (resultSL != SL_RESULT_SUCCESS) { 39212 ma_device_uninit__opensl(pDevice); 39213 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); 39214 return ma_result_from_OpenSL(resultSL); 39215 } 39216 39217 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); 39218 if (resultSL != SL_RESULT_SUCCESS) { 39219 ma_device_uninit__opensl(pDevice); 39220 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); 39221 return ma_result_from_OpenSL(resultSL); 39222 } 39223 39224 /* Set the output device. */ 39225 if (pDescriptorPlayback->pDeviceID != NULL) { 39226 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; 39227 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); 39228 } 39229 39230 queue.numBuffers = pDescriptorPlayback->periodCount; 39231 39232 source.pLocator = &queue; 39233 source.pFormat = (SLDataFormat_PCM*)&pcm; 39234 39235 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; 39236 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; 39237 39238 sink.pLocator = &outmixLocator; 39239 sink.pFormat = NULL; 39240 39241 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); 39242 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { 39243 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ 39244 pcm.formatType = SL_DATAFORMAT_PCM; 39245 pcm.numChannels = 2; 39246 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; 39247 pcm.bitsPerSample = 16; 39248 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ 39249 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; 39250 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); 39251 } 39252 39253 if (resultSL != SL_RESULT_SUCCESS) { 39254 ma_device_uninit__opensl(pDevice); 39255 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); 39256 return ma_result_from_OpenSL(resultSL); 39257 } 39258 39259 39260 /* Set the stream type before realizing the player. */ 39261 if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { 39262 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); 39263 if (resultSL == SL_RESULT_SUCCESS) { 39264 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); 39265 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); 39266 if (resultSL != SL_RESULT_SUCCESS) { 39267 /* Failed to set the configuration. Just keep going. */ 39268 } 39269 } 39270 } 39271 39272 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); 39273 if (resultSL != SL_RESULT_SUCCESS) { 39274 ma_device_uninit__opensl(pDevice); 39275 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); 39276 return ma_result_from_OpenSL(resultSL); 39277 } 39278 39279 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); 39280 if (resultSL != SL_RESULT_SUCCESS) { 39281 ma_device_uninit__opensl(pDevice); 39282 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); 39283 return ma_result_from_OpenSL(resultSL); 39284 } 39285 39286 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); 39287 if (resultSL != SL_RESULT_SUCCESS) { 39288 ma_device_uninit__opensl(pDevice); 39289 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); 39290 return ma_result_from_OpenSL(resultSL); 39291 } 39292 39293 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); 39294 if (resultSL != SL_RESULT_SUCCESS) { 39295 ma_device_uninit__opensl(pDevice); 39296 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); 39297 return ma_result_from_OpenSL(resultSL); 39298 } 39299 39300 /* The internal format is determined by the "pcm" object. */ 39301 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); 39302 39303 /* Buffer. */ 39304 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); 39305 pDevice->opensl.currentBufferIndexPlayback = 0; 39306 39307 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; 39308 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); 39309 if (pDevice->opensl.pBufferPlayback == NULL) { 39310 ma_device_uninit__opensl(pDevice); 39311 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); 39312 return MA_OUT_OF_MEMORY; 39313 } 39314 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); 39315 } 39316 39317 return MA_SUCCESS; 39318 #else 39319 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ 39320 #endif 39321 } 39322 39323 static ma_result ma_device_start__opensl(ma_device* pDevice) 39324 { 39325 SLresult resultSL; 39326 size_t periodSizeInBytes; 39327 ma_uint32 iPeriod; 39328 39329 MA_ASSERT(pDevice != NULL); 39330 39331 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ 39332 if (g_maOpenSLInitCounter == 0) { 39333 return MA_INVALID_OPERATION; 39334 } 39335 39336 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 39337 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); 39338 if (resultSL != SL_RESULT_SUCCESS) { 39339 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); 39340 return ma_result_from_OpenSL(resultSL); 39341 } 39342 39343 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); 39344 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { 39345 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); 39346 if (resultSL != SL_RESULT_SUCCESS) { 39347 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); 39348 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); 39349 return ma_result_from_OpenSL(resultSL); 39350 } 39351 } 39352 } 39353 39354 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 39355 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); 39356 if (resultSL != SL_RESULT_SUCCESS) { 39357 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); 39358 return ma_result_from_OpenSL(resultSL); 39359 } 39360 39361 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ 39362 if (pDevice->type == ma_device_type_duplex) { 39363 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); 39364 } else { 39365 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); 39366 } 39367 39368 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); 39369 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { 39370 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); 39371 if (resultSL != SL_RESULT_SUCCESS) { 39372 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); 39373 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); 39374 return ma_result_from_OpenSL(resultSL); 39375 } 39376 } 39377 } 39378 39379 return MA_SUCCESS; 39380 } 39381 39382 static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) 39383 { 39384 SLAndroidSimpleBufferQueueItf pBufferQueue; 39385 39386 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); 39387 39388 if (pDevice->type == ma_device_type_capture) { 39389 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; 39390 pDevice->opensl.isDrainingCapture = MA_TRUE; 39391 } else { 39392 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; 39393 pDevice->opensl.isDrainingPlayback = MA_TRUE; 39394 } 39395 39396 for (;;) { 39397 SLAndroidSimpleBufferQueueState state; 39398 39399 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); 39400 if (state.count == 0) { 39401 break; 39402 } 39403 39404 ma_sleep(10); 39405 } 39406 39407 if (pDevice->type == ma_device_type_capture) { 39408 pDevice->opensl.isDrainingCapture = MA_FALSE; 39409 } else { 39410 pDevice->opensl.isDrainingPlayback = MA_FALSE; 39411 } 39412 39413 return MA_SUCCESS; 39414 } 39415 39416 static ma_result ma_device_stop__opensl(ma_device* pDevice) 39417 { 39418 SLresult resultSL; 39419 39420 MA_ASSERT(pDevice != NULL); 39421 39422 MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ 39423 if (g_maOpenSLInitCounter == 0) { 39424 return MA_INVALID_OPERATION; 39425 } 39426 39427 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { 39428 ma_device_drain__opensl(pDevice, ma_device_type_capture); 39429 39430 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); 39431 if (resultSL != SL_RESULT_SUCCESS) { 39432 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); 39433 return ma_result_from_OpenSL(resultSL); 39434 } 39435 39436 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); 39437 } 39438 39439 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 39440 ma_device_drain__opensl(pDevice, ma_device_type_playback); 39441 39442 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); 39443 if (resultSL != SL_RESULT_SUCCESS) { 39444 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); 39445 return ma_result_from_OpenSL(resultSL); 39446 } 39447 39448 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); 39449 } 39450 39451 /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ 39452 ma_device__on_notification_stopped(pDevice); 39453 39454 return MA_SUCCESS; 39455 } 39456 39457 39458 static ma_result ma_context_uninit__opensl(ma_context* pContext) 39459 { 39460 MA_ASSERT(pContext != NULL); 39461 MA_ASSERT(pContext->backend == ma_backend_opensl); 39462 (void)pContext; 39463 39464 /* Uninit global data. */ 39465 ma_spinlock_lock(&g_maOpenSLSpinlock); 39466 { 39467 MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ 39468 39469 g_maOpenSLInitCounter -= 1; 39470 if (g_maOpenSLInitCounter == 0) { 39471 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); 39472 } 39473 } 39474 ma_spinlock_unlock(&g_maOpenSLSpinlock); 39475 39476 return MA_SUCCESS; 39477 } 39478 39479 static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) 39480 { 39481 /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ 39482 ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); 39483 if (p == NULL) { 39484 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); 39485 return MA_NO_BACKEND; 39486 } 39487 39488 *pHandle = *p; 39489 return MA_SUCCESS; 39490 } 39491 39492 static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) 39493 { 39494 g_maOpenSLInitCounter += 1; 39495 if (g_maOpenSLInitCounter == 1) { 39496 SLresult resultSL; 39497 39498 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); 39499 if (resultSL != SL_RESULT_SUCCESS) { 39500 g_maOpenSLInitCounter -= 1; 39501 return ma_result_from_OpenSL(resultSL); 39502 } 39503 39504 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); 39505 39506 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); 39507 if (resultSL != SL_RESULT_SUCCESS) { 39508 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); 39509 g_maOpenSLInitCounter -= 1; 39510 return ma_result_from_OpenSL(resultSL); 39511 } 39512 } 39513 39514 return MA_SUCCESS; 39515 } 39516 39517 static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 39518 { 39519 ma_result result; 39520 39521 #if !defined(MA_NO_RUNTIME_LINKING) 39522 size_t i; 39523 const char* libOpenSLESNames[] = { 39524 "libOpenSLES.so" 39525 }; 39526 #endif 39527 39528 MA_ASSERT(pContext != NULL); 39529 39530 (void)pConfig; 39531 39532 #if !defined(MA_NO_RUNTIME_LINKING) 39533 /* 39534 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One 39535 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime 39536 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any 39537 references to the symbols and will hopefully skip the checks. 39538 */ 39539 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { 39540 pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); 39541 if (pContext->opensl.libOpenSLES != NULL) { 39542 break; 39543 } 39544 } 39545 39546 if (pContext->opensl.libOpenSLES == NULL) { 39547 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); 39548 return MA_NO_BACKEND; 39549 } 39550 39551 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); 39552 if (result != MA_SUCCESS) { 39553 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39554 return result; 39555 } 39556 39557 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); 39558 if (result != MA_SUCCESS) { 39559 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39560 return result; 39561 } 39562 39563 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); 39564 if (result != MA_SUCCESS) { 39565 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39566 return result; 39567 } 39568 39569 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); 39570 if (result != MA_SUCCESS) { 39571 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39572 return result; 39573 } 39574 39575 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); 39576 if (result != MA_SUCCESS) { 39577 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39578 return result; 39579 } 39580 39581 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); 39582 if (result != MA_SUCCESS) { 39583 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39584 return result; 39585 } 39586 39587 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); 39588 if (result != MA_SUCCESS) { 39589 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39590 return result; 39591 } 39592 39593 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); 39594 if (pContext->opensl.slCreateEngine == NULL) { 39595 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39596 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); 39597 return MA_NO_BACKEND; 39598 } 39599 #else 39600 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; 39601 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; 39602 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; 39603 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; 39604 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; 39605 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; 39606 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; 39607 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; 39608 #endif 39609 39610 39611 /* Initialize global data first if applicable. */ 39612 ma_spinlock_lock(&g_maOpenSLSpinlock); 39613 { 39614 result = ma_context_init_engine_nolock__opensl(pContext); 39615 } 39616 ma_spinlock_unlock(&g_maOpenSLSpinlock); 39617 39618 if (result != MA_SUCCESS) { 39619 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); 39620 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); 39621 return result; 39622 } 39623 39624 pCallbacks->onContextInit = ma_context_init__opensl; 39625 pCallbacks->onContextUninit = ma_context_uninit__opensl; 39626 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; 39627 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; 39628 pCallbacks->onDeviceInit = ma_device_init__opensl; 39629 pCallbacks->onDeviceUninit = ma_device_uninit__opensl; 39630 pCallbacks->onDeviceStart = ma_device_start__opensl; 39631 pCallbacks->onDeviceStop = ma_device_stop__opensl; 39632 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ 39633 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ 39634 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ 39635 39636 return MA_SUCCESS; 39637 } 39638 #endif /* OpenSL|ES */ 39639 39640 39641 /****************************************************************************** 39642 39643 Web Audio Backend 39644 39645 ******************************************************************************/ 39646 #ifdef MA_HAS_WEBAUDIO 39647 #include <emscripten/emscripten.h> 39648 39649 #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) 39650 #include <emscripten/webaudio.h> 39651 #define MA_SUPPORT_AUDIO_WORKLETS 39652 #endif 39653 39654 /* 39655 TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. 39656 */ 39657 #if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) 39658 #define MA_USE_AUDIO_WORKLETS 39659 #endif 39660 39661 /* The thread stack size must be a multiple of 16. */ 39662 #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 39663 #define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 39664 #endif 39665 39666 #if defined(MA_USE_AUDIO_WORKLETS) 39667 #define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" 39668 #define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" 39669 #define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" 39670 #endif 39671 39672 static ma_bool32 ma_is_capture_supported__webaudio() 39673 { 39674 return EM_ASM_INT({ 39675 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); 39676 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ 39677 } 39678 39679 #ifdef __cplusplus 39680 extern "C" { 39681 #endif 39682 void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 39683 { 39684 return ma_malloc(sz, pAllocationCallbacks); 39685 } 39686 39687 void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 39688 { 39689 ma_free(p, pAllocationCallbacks); 39690 } 39691 39692 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) 39693 { 39694 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); 39695 } 39696 39697 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) 39698 { 39699 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); 39700 } 39701 #ifdef __cplusplus 39702 } 39703 #endif 39704 39705 static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 39706 { 39707 ma_bool32 cbResult = MA_TRUE; 39708 39709 MA_ASSERT(pContext != NULL); 39710 MA_ASSERT(callback != NULL); 39711 39712 /* Only supporting default devices for now. */ 39713 39714 /* Playback. */ 39715 if (cbResult) { 39716 ma_device_info deviceInfo; 39717 MA_ZERO_OBJECT(&deviceInfo); 39718 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 39719 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ 39720 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); 39721 } 39722 39723 /* Capture. */ 39724 if (cbResult) { 39725 if (ma_is_capture_supported__webaudio()) { 39726 ma_device_info deviceInfo; 39727 MA_ZERO_OBJECT(&deviceInfo); 39728 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 39729 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ 39730 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); 39731 } 39732 } 39733 39734 return MA_SUCCESS; 39735 } 39736 39737 static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 39738 { 39739 MA_ASSERT(pContext != NULL); 39740 39741 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { 39742 return MA_NO_DEVICE; 39743 } 39744 39745 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); 39746 39747 /* Only supporting default devices for now. */ 39748 (void)pDeviceID; 39749 if (deviceType == ma_device_type_playback) { 39750 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 39751 } else { 39752 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 39753 } 39754 39755 /* Only supporting default devices. */ 39756 pDeviceInfo->isDefault = MA_TRUE; 39757 39758 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ 39759 pDeviceInfo->nativeDataFormats[0].flags = 0; 39760 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; 39761 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ 39762 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ 39763 try { 39764 var temp = new (window.AudioContext || window.webkitAudioContext)(); 39765 var sampleRate = temp.sampleRate; 39766 temp.close(); 39767 return sampleRate; 39768 } catch(e) { 39769 return 0; 39770 } 39771 }, 0); /* Must pass in a dummy argument for C99 compatibility. */ 39772 39773 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { 39774 return MA_NO_DEVICE; 39775 } 39776 39777 pDeviceInfo->nativeDataFormatCount = 1; 39778 39779 return MA_SUCCESS; 39780 } 39781 39782 static ma_result ma_device_uninit__webaudio(ma_device* pDevice) 39783 { 39784 MA_ASSERT(pDevice != NULL); 39785 39786 #if defined(MA_USE_AUDIO_WORKLETS) 39787 { 39788 EM_ASM({ 39789 var device = miniaudio.get_device_by_index($0); 39790 39791 if (device.streamNode !== undefined) { 39792 device.streamNode.disconnect(); 39793 device.streamNode = undefined; 39794 } 39795 }, pDevice->webaudio.deviceIndex); 39796 39797 emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); 39798 emscripten_destroy_audio_context(pDevice->webaudio.audioContext); 39799 ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); 39800 } 39801 #else 39802 { 39803 EM_ASM({ 39804 var device = miniaudio.get_device_by_index($0); 39805 39806 /* Make sure all nodes are disconnected and marked for collection. */ 39807 if (device.scriptNode !== undefined) { 39808 device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ 39809 device.scriptNode.disconnect(); 39810 device.scriptNode = undefined; 39811 } 39812 39813 if (device.streamNode !== undefined) { 39814 device.streamNode.disconnect(); 39815 device.streamNode = undefined; 39816 } 39817 39818 /* 39819 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want 39820 to clear the callback before closing. 39821 */ 39822 device.webaudio.close(); 39823 device.webaudio = undefined; 39824 device.pDevice = undefined; 39825 }, pDevice->webaudio.deviceIndex); 39826 } 39827 #endif 39828 39829 /* Clean up the device on the JS side. */ 39830 EM_ASM({ 39831 miniaudio.untrack_device_by_index($0); 39832 }, pDevice->webaudio.deviceIndex); 39833 39834 ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); 39835 39836 return MA_SUCCESS; 39837 } 39838 39839 #if !defined(MA_USE_AUDIO_WORKLETS) 39840 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) 39841 { 39842 /* 39843 There have been reports of the default buffer size being too small on some browsers. If we're using 39844 the default buffer size, we'll make sure the period size is bigger than our standard defaults. 39845 */ 39846 ma_uint32 periodSizeInFrames; 39847 39848 if (nativeSampleRate == 0) { 39849 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; 39850 } 39851 39852 if (pDescriptor->periodSizeInFrames == 0) { 39853 if (pDescriptor->periodSizeInMilliseconds == 0) { 39854 if (performanceProfile == ma_performance_profile_low_latency) { 39855 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ 39856 } else { 39857 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); 39858 } 39859 } else { 39860 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); 39861 } 39862 } else { 39863 periodSizeInFrames = pDescriptor->periodSizeInFrames; 39864 } 39865 39866 /* The size of the buffer must be a power of 2 and between 256 and 16384. */ 39867 if (periodSizeInFrames < 256) { 39868 periodSizeInFrames = 256; 39869 } else if (periodSizeInFrames > 16384) { 39870 periodSizeInFrames = 16384; 39871 } else { 39872 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); 39873 } 39874 39875 return periodSizeInFrames; 39876 } 39877 #endif 39878 39879 39880 #if defined(MA_USE_AUDIO_WORKLETS) 39881 typedef struct 39882 { 39883 ma_device* pDevice; 39884 const ma_device_config* pConfig; 39885 ma_device_descriptor* pDescriptorPlayback; 39886 ma_device_descriptor* pDescriptorCapture; 39887 } ma_audio_worklet_thread_initialized_data; 39888 39889 static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) 39890 { 39891 ma_device* pDevice = (ma_device*)pUserData; 39892 ma_uint32 frameCount; 39893 39894 (void)paramCount; 39895 (void)pParams; 39896 39897 if (ma_device_get_state(pDevice) != ma_device_state_started) { 39898 return EM_TRUE; 39899 } 39900 39901 /* 39902 The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels 39903 like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer 39904 to variables instead of a hard coded number. In any case, will follow along for the time being. 39905 39906 Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio 39907 for further processing. 39908 */ 39909 frameCount = 128; 39910 39911 if (inputCount > 0) { 39912 /* Input data needs to be interleaved before we hand it to the client. */ 39913 for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { 39914 for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { 39915 pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; 39916 } 39917 } 39918 39919 ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); 39920 } 39921 39922 if (outputCount > 0) { 39923 /* If it's a capture-only device, we'll need to output silence. */ 39924 if (pDevice->type == ma_device_type_capture) { 39925 MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); 39926 } else { 39927 ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); 39928 39929 /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ 39930 for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { 39931 for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { 39932 pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; 39933 } 39934 } 39935 } 39936 } 39937 39938 return EM_TRUE; 39939 } 39940 39941 39942 static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) 39943 { 39944 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; 39945 EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; 39946 int channels = 0; 39947 size_t intermediaryBufferSizeInFrames; 39948 int sampleRate; 39949 39950 if (success == EM_FALSE) { 39951 pParameters->pDevice->webaudio.initResult = MA_ERROR; 39952 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); 39953 return; 39954 } 39955 39956 /* The next step is to initialize the audio worklet node. */ 39957 MA_ZERO_OBJECT(&audioWorkletOptions); 39958 39959 /* 39960 The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel 39961 count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an 39962 output channel count on the capture side. This is slightly confusing for capture mode because intuitively you 39963 wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have 39964 proper control over the channel count. In the capture case, we'll have to output silence to it's output node. 39965 */ 39966 if (pParameters->pConfig->deviceType == ma_device_type_capture) { 39967 channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); 39968 audioWorkletOptions.numberOfInputs = 1; 39969 } else { 39970 channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); 39971 39972 if (pParameters->pConfig->deviceType == ma_device_type_duplex) { 39973 audioWorkletOptions.numberOfInputs = 1; 39974 } else { 39975 audioWorkletOptions.numberOfInputs = 0; 39976 } 39977 } 39978 39979 audioWorkletOptions.numberOfOutputs = 1; 39980 audioWorkletOptions.outputChannelCounts = &channels; 39981 39982 39983 /* 39984 Now that we know the channel count to use we can allocate the intermediary buffer. The 39985 intermediary buffer is used for interleaving and deinterleaving. 39986 */ 39987 intermediaryBufferSizeInFrames = 128; 39988 39989 pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); 39990 if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { 39991 pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; 39992 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); 39993 return; 39994 } 39995 39996 39997 pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); 39998 39999 /* With the audio worklet initialized we can now attach it to the graph. */ 40000 if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { 40001 ma_result attachmentResult = (ma_result)EM_ASM_INT({ 40002 var getUserMediaResult = 0; 40003 var audioWorklet = emscriptenGetAudioObject($0); 40004 var audioContext = emscriptenGetAudioObject($1); 40005 40006 navigator.mediaDevices.getUserMedia({audio:true, video:false}) 40007 .then(function(stream) { 40008 audioContext.streamNode = audioContext.createMediaStreamSource(stream); 40009 audioContext.streamNode.connect(audioWorklet); 40010 audioWorklet.connect(audioContext.destination); 40011 getUserMediaResult = 0; /* 0 = MA_SUCCESS */ 40012 }) 40013 .catch(function(error) { 40014 console.log("navigator.mediaDevices.getUserMedia Failed: " + error); 40015 getUserMediaResult = -1; /* -1 = MA_ERROR */ 40016 }); 40017 40018 return getUserMediaResult; 40019 }, pParameters->pDevice->webaudio.audioWorklet, audioContext); 40020 40021 if (attachmentResult != MA_SUCCESS) { 40022 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); 40023 emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); 40024 pParameters->pDevice->webaudio.initResult = attachmentResult; 40025 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); 40026 return; 40027 } 40028 } 40029 40030 /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ 40031 if (pParameters->pConfig->deviceType == ma_device_type_playback) { 40032 ma_result attachmentResult = (ma_result)EM_ASM_INT({ 40033 var audioWorklet = emscriptenGetAudioObject($0); 40034 var audioContext = emscriptenGetAudioObject($1); 40035 audioWorklet.connect(audioContext.destination); 40036 return 0; /* 0 = MA_SUCCESS */ 40037 }, pParameters->pDevice->webaudio.audioWorklet, audioContext); 40038 40039 if (attachmentResult != MA_SUCCESS) { 40040 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); 40041 pParameters->pDevice->webaudio.initResult = attachmentResult; 40042 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); 40043 return; 40044 } 40045 } 40046 40047 /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ 40048 sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); 40049 40050 if (pParameters->pDescriptorCapture != NULL) { 40051 pParameters->pDescriptorCapture->format = ma_format_f32; 40052 pParameters->pDescriptorCapture->channels = (ma_uint32)channels; 40053 pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; 40054 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); 40055 pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; 40056 pParameters->pDescriptorCapture->periodCount = 1; 40057 } 40058 40059 if (pParameters->pDescriptorPlayback != NULL) { 40060 pParameters->pDescriptorPlayback->format = ma_format_f32; 40061 pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; 40062 pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; 40063 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); 40064 pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; 40065 pParameters->pDescriptorPlayback->periodCount = 1; 40066 } 40067 40068 /* At this point we're done and we can return. */ 40069 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); 40070 pParameters->pDevice->webaudio.initResult = MA_SUCCESS; 40071 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); 40072 } 40073 40074 static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) 40075 { 40076 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; 40077 WebAudioWorkletProcessorCreateOptions workletProcessorOptions; 40078 40079 MA_ASSERT(pParameters != NULL); 40080 40081 if (success == EM_FALSE) { 40082 pParameters->pDevice->webaudio.initResult = MA_ERROR; 40083 return; 40084 } 40085 40086 MA_ZERO_OBJECT(&workletProcessorOptions); 40087 workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ 40088 40089 emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); 40090 } 40091 #endif 40092 40093 static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) 40094 { 40095 if (pConfig->deviceType == ma_device_type_loopback) { 40096 return MA_DEVICE_TYPE_NOT_SUPPORTED; 40097 } 40098 40099 /* No exclusive mode with Web Audio. */ 40100 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || 40101 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { 40102 return MA_SHARE_MODE_NOT_SUPPORTED; 40103 } 40104 40105 /* 40106 With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so 40107 it might be worthwhile to look into that as well. 40108 */ 40109 #if defined(MA_USE_AUDIO_WORKLETS) 40110 { 40111 EmscriptenWebAudioCreateAttributes audioContextAttributes; 40112 ma_audio_worklet_thread_initialized_data* pInitParameters; 40113 void* pStackBuffer; 40114 40115 if (pConfig->performanceProfile == ma_performance_profile_conservative) { 40116 audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; 40117 } else { 40118 audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; 40119 } 40120 40121 /* 40122 In my testing, Firefox does not seem to capture audio data properly if the sample rate is set 40123 to anything other than 48K. This does not seem to be the case for other browsers. For this reason, 40124 if the device type is anything other than playback, we'll leave the sample rate as-is and let the 40125 browser pick the appropriate rate for us. 40126 */ 40127 if (pConfig->deviceType == ma_device_type_playback) { 40128 audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; 40129 } else { 40130 audioContextAttributes.sampleRate = 0; 40131 } 40132 40133 /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ 40134 pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); 40135 40136 40137 /* 40138 With the context created we can now create the worklet. We can only have a single worklet per audio 40139 context which means we'll need to craft this appropriately to handle duplex devices correctly. 40140 */ 40141 40142 /* 40143 We now need to create a worker thread. This is a bit weird because we need to allocate our 40144 own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to 40145 allocate this on the heap to keep it simple. 40146 */ 40147 pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); 40148 if (pStackBuffer == NULL) { 40149 emscripten_destroy_audio_context(pDevice->webaudio.audioContext); 40150 return MA_OUT_OF_MEMORY; 40151 } 40152 40153 /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ 40154 pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); 40155 if (pInitParameters == NULL) { 40156 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); 40157 emscripten_destroy_audio_context(pDevice->webaudio.audioContext); 40158 return MA_OUT_OF_MEMORY; 40159 } 40160 40161 pInitParameters->pDevice = pDevice; 40162 pInitParameters->pConfig = pConfig; 40163 pInitParameters->pDescriptorPlayback = pDescriptorPlayback; 40164 pInitParameters->pDescriptorCapture = pDescriptorCapture; 40165 40166 /* 40167 We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of 40168 the Emscripten WebAudio stuff is asynchronous. 40169 */ 40170 pDevice->webaudio.initResult = MA_BUSY; 40171 { 40172 emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); 40173 } 40174 while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ 40175 40176 /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ 40177 if (pDevice->webaudio.initResult != MA_SUCCESS) { 40178 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); 40179 emscripten_destroy_audio_context(pDevice->webaudio.audioContext); 40180 return pDevice->webaudio.initResult; 40181 } 40182 40183 /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ 40184 pDevice->webaudio.deviceIndex = EM_ASM_INT({ 40185 return miniaudio.track_device({ 40186 webaudio: emscriptenGetAudioObject($0), 40187 state: 1 /* 1 = ma_device_state_stopped */ 40188 }); 40189 }, pDevice->webaudio.audioContext); 40190 40191 return MA_SUCCESS; 40192 } 40193 #else 40194 { 40195 /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ 40196 ma_uint32 deviceIndex; 40197 ma_uint32 channels; 40198 ma_uint32 sampleRate; 40199 ma_uint32 periodSizeInFrames; 40200 40201 /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ 40202 if (pConfig->deviceType == ma_device_type_capture) { 40203 channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; 40204 } else { 40205 channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; 40206 } 40207 40208 /* 40209 When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's 40210 native rate. For this reason we're leaving the sample rate untouched for capture devices. 40211 */ 40212 if (pConfig->deviceType == ma_device_type_playback) { 40213 sampleRate = pDescriptorPlayback->sampleRate; 40214 } else { 40215 sampleRate = 0; /* Let the browser decide when capturing. */ 40216 } 40217 40218 /* The period size needs to be a power of 2. */ 40219 if (pConfig->deviceType == ma_device_type_capture) { 40220 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); 40221 } else { 40222 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); 40223 } 40224 40225 /* We need an intermediary buffer for doing interleaving and deinterleaving. */ 40226 pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); 40227 if (pDevice->webaudio.pIntermediaryBuffer == NULL) { 40228 return MA_OUT_OF_MEMORY; 40229 } 40230 40231 deviceIndex = EM_ASM_INT({ 40232 var deviceType = $0; 40233 var channels = $1; 40234 var sampleRate = $2; 40235 var bufferSize = $3; 40236 var pIntermediaryBuffer = $4; 40237 var pDevice = $5; 40238 40239 if (typeof(window.miniaudio) === 'undefined') { 40240 return -1; /* Context not initialized. */ 40241 } 40242 40243 var device = {}; 40244 40245 /* First thing we need is an AudioContext. */ 40246 var audioContextOptions = {}; 40247 if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { 40248 audioContextOptions.sampleRate = sampleRate; 40249 } 40250 40251 device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); 40252 device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ 40253 device.state = window.miniaudio.device_state.stopped; 40254 40255 /* 40256 We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we 40257 need to specify an output and configure the channel count there. 40258 */ 40259 var channelCountIn = 0; 40260 var channelCountOut = channels; 40261 if (deviceType != window.miniaudio.device_type.playback) { 40262 channelCountIn = channels; 40263 } 40264 40265 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); 40266 40267 /* The node processing callback. */ 40268 device.scriptNode.onaudioprocess = function(e) { 40269 if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { 40270 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); 40271 } 40272 40273 /* Do the capture side first. */ 40274 if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { 40275 /* The data must be interleaved before being processed miniaudio. */ 40276 for (var iChannel = 0; iChannel < channels; iChannel += 1) { 40277 var inputBuffer = e.inputBuffer.getChannelData(iChannel); 40278 var intermediaryBuffer = device.intermediaryBufferView; 40279 40280 for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { 40281 intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; 40282 } 40283 } 40284 40285 _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); 40286 } 40287 40288 if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { 40289 _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); 40290 40291 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { 40292 var outputBuffer = e.outputBuffer.getChannelData(iChannel); 40293 var intermediaryBuffer = device.intermediaryBufferView; 40294 40295 for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { 40296 outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; 40297 } 40298 } 40299 } else { 40300 /* It's a capture-only device. Make sure the output is silenced. */ 40301 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { 40302 e.outputBuffer.getChannelData(iChannel).fill(0.0); 40303 } 40304 } 40305 }; 40306 40307 /* Now we need to connect our node to the graph. */ 40308 if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { 40309 navigator.mediaDevices.getUserMedia({audio:true, video:false}) 40310 .then(function(stream) { 40311 device.streamNode = device.webaudio.createMediaStreamSource(stream); 40312 device.streamNode.connect(device.scriptNode); 40313 device.scriptNode.connect(device.webaudio.destination); 40314 }) 40315 .catch(function(error) { 40316 console.log("Failed to get user media: " + error); 40317 }); 40318 } 40319 40320 if (deviceType == miniaudio.device_type.playback) { 40321 device.scriptNode.connect(device.webaudio.destination); 40322 } 40323 40324 device.pDevice = pDevice; 40325 40326 return miniaudio.track_device(device); 40327 }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); 40328 40329 if (deviceIndex < 0) { 40330 return MA_FAILED_TO_OPEN_BACKEND_DEVICE; 40331 } 40332 40333 pDevice->webaudio.deviceIndex = deviceIndex; 40334 40335 /* Grab the sample rate from the audio context directly. */ 40336 sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); 40337 40338 if (pDescriptorCapture != NULL) { 40339 pDescriptorCapture->format = ma_format_f32; 40340 pDescriptorCapture->channels = channels; 40341 pDescriptorCapture->sampleRate = sampleRate; 40342 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); 40343 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; 40344 pDescriptorCapture->periodCount = 1; 40345 } 40346 40347 if (pDescriptorPlayback != NULL) { 40348 pDescriptorPlayback->format = ma_format_f32; 40349 pDescriptorPlayback->channels = channels; 40350 pDescriptorPlayback->sampleRate = sampleRate; 40351 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); 40352 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; 40353 pDescriptorPlayback->periodCount = 1; 40354 } 40355 40356 return MA_SUCCESS; 40357 } 40358 #endif 40359 } 40360 40361 static ma_result ma_device_start__webaudio(ma_device* pDevice) 40362 { 40363 MA_ASSERT(pDevice != NULL); 40364 40365 EM_ASM({ 40366 var device = miniaudio.get_device_by_index($0); 40367 device.webaudio.resume(); 40368 device.state = miniaudio.device_state.started; 40369 }, pDevice->webaudio.deviceIndex); 40370 40371 return MA_SUCCESS; 40372 } 40373 40374 static ma_result ma_device_stop__webaudio(ma_device* pDevice) 40375 { 40376 MA_ASSERT(pDevice != NULL); 40377 40378 /* 40379 From the WebAudio API documentation for AudioContext.suspend(): 40380 40381 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the 40382 destination, and then allows the system to release its claim on audio hardware. 40383 40384 I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to 40385 do any kind of explicit draining. 40386 */ 40387 EM_ASM({ 40388 var device = miniaudio.get_device_by_index($0); 40389 device.webaudio.suspend(); 40390 device.state = miniaudio.device_state.stopped; 40391 }, pDevice->webaudio.deviceIndex); 40392 40393 ma_device__on_notification_stopped(pDevice); 40394 40395 return MA_SUCCESS; 40396 } 40397 40398 static ma_result ma_context_uninit__webaudio(ma_context* pContext) 40399 { 40400 MA_ASSERT(pContext != NULL); 40401 MA_ASSERT(pContext->backend == ma_backend_webaudio); 40402 40403 (void)pContext; /* Unused. */ 40404 40405 /* Remove the global miniaudio object from window if there are no more references to it. */ 40406 EM_ASM({ 40407 if (typeof(window.miniaudio) !== 'undefined') { 40408 window.miniaudio.referenceCount -= 1; 40409 if (window.miniaudio.referenceCount === 0) { 40410 delete window.miniaudio; 40411 } 40412 } 40413 }); 40414 40415 return MA_SUCCESS; 40416 } 40417 40418 static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) 40419 { 40420 int resultFromJS; 40421 40422 MA_ASSERT(pContext != NULL); 40423 40424 (void)pConfig; /* Unused. */ 40425 40426 /* Here is where our global JavaScript object is initialized. */ 40427 resultFromJS = EM_ASM_INT({ 40428 if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { 40429 return 0; /* Web Audio not supported. */ 40430 } 40431 40432 if (typeof(window.miniaudio) === 'undefined') { 40433 window.miniaudio = { 40434 referenceCount: 0 40435 }; 40436 40437 /* Device types. */ 40438 window.miniaudio.device_type = {}; 40439 window.miniaudio.device_type.playback = $0; 40440 window.miniaudio.device_type.capture = $1; 40441 window.miniaudio.device_type.duplex = $2; 40442 40443 /* Device states. */ 40444 window.miniaudio.device_state = {}; 40445 window.miniaudio.device_state.stopped = $3; 40446 window.miniaudio.device_state.started = $4; 40447 40448 /* Device cache for mapping devices to indexes for JavaScript/C interop. */ 40449 miniaudio.devices = []; 40450 40451 miniaudio.track_device = function(device) { 40452 /* Try inserting into a free slot first. */ 40453 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { 40454 if (miniaudio.devices[iDevice] == null) { 40455 miniaudio.devices[iDevice] = device; 40456 return iDevice; 40457 } 40458 } 40459 40460 /* Getting here means there is no empty slots in the array so we just push to the end. */ 40461 miniaudio.devices.push(device); 40462 return miniaudio.devices.length - 1; 40463 }; 40464 40465 miniaudio.untrack_device_by_index = function(deviceIndex) { 40466 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ 40467 miniaudio.devices[deviceIndex] = null; 40468 40469 /* Trim the array if possible. */ 40470 while (miniaudio.devices.length > 0) { 40471 if (miniaudio.devices[miniaudio.devices.length-1] == null) { 40472 miniaudio.devices.pop(); 40473 } else { 40474 break; 40475 } 40476 } 40477 }; 40478 40479 miniaudio.untrack_device = function(device) { 40480 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { 40481 if (miniaudio.devices[iDevice] == device) { 40482 return miniaudio.untrack_device_by_index(iDevice); 40483 } 40484 } 40485 }; 40486 40487 miniaudio.get_device_by_index = function(deviceIndex) { 40488 return miniaudio.devices[deviceIndex]; 40489 }; 40490 40491 miniaudio.unlock_event_types = (function(){ 40492 return ['touchend', 'click']; 40493 })(); 40494 40495 miniaudio.unlock = function() { 40496 for(var i = 0; i < miniaudio.devices.length; ++i) { 40497 var device = miniaudio.devices[i]; 40498 if (device != null && 40499 device.webaudio != null && 40500 device.state === window.miniaudio.device_state.started) { 40501 40502 device.webaudio.resume().then(() => { 40503 Module._ma_device__on_notification_unlocked(device.pDevice); 40504 }, 40505 (error) => {console.error("Failed to resume audiocontext", error); 40506 }); 40507 } 40508 } 40509 miniaudio.unlock_event_types.map(function(event_type) { 40510 document.removeEventListener(event_type, miniaudio.unlock, true); 40511 }); 40512 }; 40513 40514 miniaudio.unlock_event_types.map(function(event_type) { 40515 document.addEventListener(event_type, miniaudio.unlock, true); 40516 }); 40517 } 40518 40519 window.miniaudio.referenceCount += 1; 40520 40521 return 1; 40522 }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); 40523 40524 if (resultFromJS != 1) { 40525 return MA_FAILED_TO_INIT_BACKEND; 40526 } 40527 40528 pCallbacks->onContextInit = ma_context_init__webaudio; 40529 pCallbacks->onContextUninit = ma_context_uninit__webaudio; 40530 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; 40531 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; 40532 pCallbacks->onDeviceInit = ma_device_init__webaudio; 40533 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; 40534 pCallbacks->onDeviceStart = ma_device_start__webaudio; 40535 pCallbacks->onDeviceStop = ma_device_stop__webaudio; 40536 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ 40537 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ 40538 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ 40539 40540 return MA_SUCCESS; 40541 } 40542 #endif /* Web Audio */ 40543 40544 40545 40546 static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) 40547 { 40548 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ 40549 if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { 40550 ma_uint32 iChannel; 40551 40552 if (channels == 0 || channels > MA_MAX_CHANNELS) { 40553 return MA_FALSE; /* Channel count out of range. */ 40554 } 40555 40556 /* A channel cannot be present in the channel map more than once. */ 40557 for (iChannel = 0; iChannel < channels; ++iChannel) { 40558 ma_uint32 jChannel; 40559 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { 40560 if (pChannelMap[iChannel] == pChannelMap[jChannel]) { 40561 return MA_FALSE; 40562 } 40563 } 40564 } 40565 } 40566 40567 return MA_TRUE; 40568 } 40569 40570 40571 static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) 40572 { 40573 MA_ASSERT(pContext != NULL); 40574 40575 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { 40576 if (pContext->callbacks.onDeviceDataLoop == NULL) { 40577 return MA_TRUE; 40578 } else { 40579 return MA_FALSE; 40580 } 40581 } else { 40582 return MA_FALSE; 40583 } 40584 } 40585 40586 40587 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) 40588 { 40589 ma_result result; 40590 40591 MA_ASSERT(pDevice != NULL); 40592 40593 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 40594 if (pDevice->capture.format == ma_format_unknown) { 40595 pDevice->capture.format = pDevice->capture.internalFormat; 40596 } 40597 if (pDevice->capture.channels == 0) { 40598 pDevice->capture.channels = pDevice->capture.internalChannels; 40599 } 40600 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { 40601 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); 40602 if (pDevice->capture.internalChannels == pDevice->capture.channels) { 40603 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); 40604 } else { 40605 if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { 40606 ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); 40607 } else { 40608 ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); 40609 } 40610 } 40611 } 40612 } 40613 40614 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 40615 if (pDevice->playback.format == ma_format_unknown) { 40616 pDevice->playback.format = pDevice->playback.internalFormat; 40617 } 40618 if (pDevice->playback.channels == 0) { 40619 pDevice->playback.channels = pDevice->playback.internalChannels; 40620 } 40621 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { 40622 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); 40623 if (pDevice->playback.internalChannels == pDevice->playback.channels) { 40624 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); 40625 } else { 40626 if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { 40627 ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); 40628 } else { 40629 ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); 40630 } 40631 } 40632 } 40633 } 40634 40635 if (pDevice->sampleRate == 0) { 40636 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 40637 pDevice->sampleRate = pDevice->capture.internalSampleRate; 40638 } else { 40639 pDevice->sampleRate = pDevice->playback.internalSampleRate; 40640 } 40641 } 40642 40643 /* Data converters. */ 40644 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 40645 /* Converting from internal device format to client format. */ 40646 ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); 40647 converterConfig.formatIn = pDevice->capture.internalFormat; 40648 converterConfig.channelsIn = pDevice->capture.internalChannels; 40649 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; 40650 converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; 40651 converterConfig.formatOut = pDevice->capture.format; 40652 converterConfig.channelsOut = pDevice->capture.channels; 40653 converterConfig.sampleRateOut = pDevice->sampleRate; 40654 converterConfig.pChannelMapOut = pDevice->capture.channelMap; 40655 converterConfig.channelMixMode = pDevice->capture.channelMixMode; 40656 converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; 40657 converterConfig.allowDynamicSampleRate = MA_FALSE; 40658 converterConfig.resampling.algorithm = pDevice->resampling.algorithm; 40659 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; 40660 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; 40661 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; 40662 40663 /* Make sure the old converter is uninitialized first. */ 40664 if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { 40665 ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); 40666 } 40667 40668 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); 40669 if (result != MA_SUCCESS) { 40670 return result; 40671 } 40672 } 40673 40674 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 40675 /* Converting from client format to device format. */ 40676 ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); 40677 converterConfig.formatIn = pDevice->playback.format; 40678 converterConfig.channelsIn = pDevice->playback.channels; 40679 converterConfig.sampleRateIn = pDevice->sampleRate; 40680 converterConfig.pChannelMapIn = pDevice->playback.channelMap; 40681 converterConfig.formatOut = pDevice->playback.internalFormat; 40682 converterConfig.channelsOut = pDevice->playback.internalChannels; 40683 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; 40684 converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; 40685 converterConfig.channelMixMode = pDevice->playback.channelMixMode; 40686 converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; 40687 converterConfig.allowDynamicSampleRate = MA_FALSE; 40688 converterConfig.resampling.algorithm = pDevice->resampling.algorithm; 40689 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; 40690 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; 40691 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; 40692 40693 /* Make sure the old converter is uninitialized first. */ 40694 if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { 40695 ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); 40696 } 40697 40698 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); 40699 if (result != MA_SUCCESS) { 40700 return result; 40701 } 40702 } 40703 40704 40705 /* 40706 If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's 40707 a couple of situations where we'll need a heap allocated cache. 40708 40709 The first is a duplex device for backends that use a callback for data delivery. The reason 40710 this is needed is that the input stage needs to have a buffer to place the input data while it 40711 waits for the playback stage, after which the miniaudio data callback will get fired. This is 40712 not needed for backends that use a blocking API because miniaudio manages temporary buffers on 40713 the stack to achieve this. 40714 40715 The other situation is when the data converter does not have the ability to query the number 40716 of input frames that are required in order to process a given number of output frames. When 40717 performing data conversion, it's useful if miniaudio know exactly how many frames it needs 40718 from the client in order to generate a given number of output frames. This way, only exactly 40719 the number of frames are needed to be read from the client which means no cache is necessary. 40720 On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read 40721 in fixed sized chunks and then cache any residual unused input frames, those of which will be 40722 processed at a later stage. 40723 */ 40724 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 40725 ma_uint64 unused; 40726 40727 pDevice->playback.inputCacheConsumed = 0; 40728 pDevice->playback.inputCacheRemaining = 0; 40729 40730 if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ 40731 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ 40732 { 40733 /* We need a heap allocated cache. We want to size this based on the period size. */ 40734 void* pNewInputCache; 40735 ma_uint64 newInputCacheCap; 40736 ma_uint64 newInputCacheSizeInBytes; 40737 40738 newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); 40739 40740 newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 40741 if (newInputCacheSizeInBytes > MA_SIZE_MAX) { 40742 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); 40743 pDevice->playback.pInputCache = NULL; 40744 pDevice->playback.inputCacheCap = 0; 40745 return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ 40746 } 40747 40748 pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); 40749 if (pNewInputCache == NULL) { 40750 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); 40751 pDevice->playback.pInputCache = NULL; 40752 pDevice->playback.inputCacheCap = 0; 40753 return MA_OUT_OF_MEMORY; 40754 } 40755 40756 pDevice->playback.pInputCache = pNewInputCache; 40757 pDevice->playback.inputCacheCap = newInputCacheCap; 40758 } else { 40759 /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ 40760 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); 40761 pDevice->playback.pInputCache = NULL; 40762 pDevice->playback.inputCacheCap = 0; 40763 } 40764 } 40765 40766 return MA_SUCCESS; 40767 } 40768 40769 MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) 40770 { 40771 ma_result result; 40772 40773 if (pDevice == NULL) { 40774 return MA_INVALID_ARGS; 40775 } 40776 40777 /* Capture. */ 40778 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 40779 if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { 40780 return MA_INVALID_ARGS; 40781 } 40782 40783 pDevice->capture.internalFormat = pDescriptorCapture->format; 40784 pDevice->capture.internalChannels = pDescriptorCapture->channels; 40785 pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; 40786 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); 40787 pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; 40788 pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; 40789 40790 if (pDevice->capture.internalPeriodSizeInFrames == 0) { 40791 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); 40792 } 40793 } 40794 40795 /* Playback. */ 40796 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 40797 if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { 40798 return MA_INVALID_ARGS; 40799 } 40800 40801 pDevice->playback.internalFormat = pDescriptorPlayback->format; 40802 pDevice->playback.internalChannels = pDescriptorPlayback->channels; 40803 pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; 40804 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); 40805 pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; 40806 pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; 40807 40808 if (pDevice->playback.internalPeriodSizeInFrames == 0) { 40809 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); 40810 } 40811 } 40812 40813 /* 40814 The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. 40815 For loopback devices, we need to retrieve the name of the playback device. 40816 */ 40817 { 40818 ma_device_info deviceInfo; 40819 40820 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { 40821 result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); 40822 if (result == MA_SUCCESS) { 40823 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); 40824 } else { 40825 /* We failed to retrieve the device info. Fall back to a default name. */ 40826 if (pDescriptorCapture->pDeviceID == NULL) { 40827 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 40828 } else { 40829 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); 40830 } 40831 } 40832 } 40833 40834 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { 40835 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); 40836 if (result == MA_SUCCESS) { 40837 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); 40838 } else { 40839 /* We failed to retrieve the device info. Fall back to a default name. */ 40840 if (pDescriptorPlayback->pDeviceID == NULL) { 40841 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 40842 } else { 40843 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); 40844 } 40845 } 40846 } 40847 } 40848 40849 /* Update data conversion. */ 40850 return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ 40851 } 40852 40853 40854 static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) 40855 { 40856 ma_device* pDevice = (ma_device*)pData; 40857 #ifdef MA_WIN32 40858 HRESULT CoInitializeResult; 40859 #endif 40860 40861 MA_ASSERT(pDevice != NULL); 40862 40863 #ifdef MA_WIN32 40864 CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); 40865 #endif 40866 40867 /* 40868 When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from 40869 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately 40870 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker 40871 thread to signal an event to know when the worker thread is ready for action. 40872 */ 40873 ma_device__set_state(pDevice, ma_device_state_stopped); 40874 ma_event_signal(&pDevice->stopEvent); 40875 40876 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ 40877 ma_result startResult; 40878 ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ 40879 40880 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ 40881 ma_event_wait(&pDevice->wakeupEvent); 40882 40883 /* Default result code. */ 40884 pDevice->workResult = MA_SUCCESS; 40885 40886 /* If the reason for the wake up is that we are terminating, just break from the loop. */ 40887 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { 40888 break; 40889 } 40890 40891 /* 40892 Getting to this point means the device is wanting to get started. The function that has requested that the device 40893 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event 40894 in both the success and error case. It's important that the state of the device is set _before_ signaling the event. 40895 */ 40896 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); 40897 40898 /* If the device has a start callback, start it now. */ 40899 if (pDevice->pContext->callbacks.onDeviceStart != NULL) { 40900 startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); 40901 } else { 40902 startResult = MA_SUCCESS; 40903 } 40904 40905 /* 40906 If starting was not successful we'll need to loop back to the start and wait for something 40907 to happen (pDevice->wakeupEvent). 40908 */ 40909 if (startResult != MA_SUCCESS) { 40910 pDevice->workResult = startResult; 40911 ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ 40912 continue; 40913 } 40914 40915 /* Make sure the state is set appropriately. */ 40916 ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ 40917 ma_event_signal(&pDevice->startEvent); 40918 40919 ma_device__on_notification_started(pDevice); 40920 40921 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { 40922 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); 40923 } else { 40924 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ 40925 ma_device_audio_thread__default_read_write(pDevice); 40926 } 40927 40928 /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ 40929 if (pDevice->pContext->callbacks.onDeviceStop != NULL) { 40930 stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); 40931 } else { 40932 stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ 40933 } 40934 40935 /* 40936 After the device has stopped, make sure an event is posted. Don't post a stopped event if 40937 stopping failed. This can happen on some backends when the underlying stream has been 40938 stopped due to the device being physically unplugged or disabled via an OS setting. 40939 */ 40940 if (stopResult == MA_SUCCESS) { 40941 ma_device__on_notification_stopped(pDevice); 40942 } 40943 40944 /* If we stopped because the device has been uninitialized, abort now. */ 40945 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { 40946 break; 40947 } 40948 40949 /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ 40950 ma_device__set_state(pDevice, ma_device_state_stopped); 40951 ma_event_signal(&pDevice->stopEvent); 40952 } 40953 40954 #ifdef MA_WIN32 40955 if (CoInitializeResult == S_OK) { 40956 ma_CoUninitialize(pDevice->pContext); 40957 } 40958 #endif 40959 40960 return (ma_thread_result)0; 40961 } 40962 40963 40964 /* Helper for determining whether or not the given device is initialized. */ 40965 static ma_bool32 ma_device__is_initialized(ma_device* pDevice) 40966 { 40967 if (pDevice == NULL) { 40968 return MA_FALSE; 40969 } 40970 40971 return ma_device_get_state(pDevice) != ma_device_state_uninitialized; 40972 } 40973 40974 40975 #ifdef MA_WIN32 40976 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) 40977 { 40978 /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ 40979 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 40980 if (pContext->win32.CoInitializeResult == S_OK) { 40981 ma_CoUninitialize(pContext); 40982 } 40983 40984 #if defined(MA_WIN32_DESKTOP) 40985 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); 40986 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); 40987 #endif 40988 40989 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); 40990 #else 40991 (void)pContext; 40992 #endif 40993 40994 return MA_SUCCESS; 40995 } 40996 40997 static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) 40998 { 40999 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) 41000 #if defined(MA_WIN32_DESKTOP) 41001 /* User32.dll */ 41002 pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); 41003 if (pContext->win32.hUser32DLL == NULL) { 41004 return MA_FAILED_TO_INIT_BACKEND; 41005 } 41006 41007 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); 41008 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); 41009 41010 41011 /* Advapi32.dll */ 41012 pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); 41013 if (pContext->win32.hAdvapi32DLL == NULL) { 41014 return MA_FAILED_TO_INIT_BACKEND; 41015 } 41016 41017 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); 41018 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); 41019 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); 41020 #endif 41021 41022 /* Ole32.dll */ 41023 pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); 41024 if (pContext->win32.hOle32DLL == NULL) { 41025 return MA_FAILED_TO_INIT_BACKEND; 41026 } 41027 41028 pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); 41029 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); 41030 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); 41031 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); 41032 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); 41033 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); 41034 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); 41035 #else 41036 (void)pContext; /* Unused. */ 41037 #endif 41038 41039 pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); 41040 return MA_SUCCESS; 41041 } 41042 #else 41043 static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) 41044 { 41045 (void)pContext; 41046 41047 return MA_SUCCESS; 41048 } 41049 41050 static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) 41051 { 41052 (void)pContext; 41053 41054 return MA_SUCCESS; 41055 } 41056 #endif 41057 41058 static ma_result ma_context_init_backend_apis(ma_context* pContext) 41059 { 41060 ma_result result; 41061 #ifdef MA_WIN32 41062 result = ma_context_init_backend_apis__win32(pContext); 41063 #else 41064 result = ma_context_init_backend_apis__nix(pContext); 41065 #endif 41066 41067 return result; 41068 } 41069 41070 static ma_result ma_context_uninit_backend_apis(ma_context* pContext) 41071 { 41072 ma_result result; 41073 #ifdef MA_WIN32 41074 result = ma_context_uninit_backend_apis__win32(pContext); 41075 #else 41076 result = ma_context_uninit_backend_apis__nix(pContext); 41077 #endif 41078 41079 return result; 41080 } 41081 41082 41083 /* The default capacity doesn't need to be too big. */ 41084 #ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 41085 #define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 41086 #endif 41087 41088 MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) 41089 { 41090 ma_device_job_thread_config config; 41091 41092 MA_ZERO_OBJECT(&config); 41093 config.noThread = MA_FALSE; 41094 config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; 41095 config.jobQueueFlags = 0; 41096 41097 return config; 41098 } 41099 41100 41101 static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) 41102 { 41103 ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; 41104 MA_ASSERT(pJobThread != NULL); 41105 41106 for (;;) { 41107 ma_result result; 41108 ma_job job; 41109 41110 result = ma_device_job_thread_next(pJobThread, &job); 41111 if (result != MA_SUCCESS) { 41112 break; 41113 } 41114 41115 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { 41116 break; 41117 } 41118 41119 ma_job_process(&job); 41120 } 41121 41122 return (ma_thread_result)0; 41123 } 41124 41125 MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) 41126 { 41127 ma_result result; 41128 ma_job_queue_config jobQueueConfig; 41129 41130 if (pJobThread == NULL) { 41131 return MA_INVALID_ARGS; 41132 } 41133 41134 MA_ZERO_OBJECT(pJobThread); 41135 41136 if (pConfig == NULL) { 41137 return MA_INVALID_ARGS; 41138 } 41139 41140 41141 /* Initialize the job queue before the thread to ensure it's in a valid state. */ 41142 jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); 41143 41144 result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); 41145 if (result != MA_SUCCESS) { 41146 return result; /* Failed to initialize job queue. */ 41147 } 41148 41149 41150 /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ 41151 if (pConfig->noThread == MA_FALSE) { 41152 result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); 41153 if (result != MA_SUCCESS) { 41154 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); 41155 return result; /* Failed to create the job thread. */ 41156 } 41157 41158 pJobThread->_hasThread = MA_TRUE; 41159 } else { 41160 pJobThread->_hasThread = MA_FALSE; 41161 } 41162 41163 41164 return MA_SUCCESS; 41165 } 41166 41167 MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) 41168 { 41169 if (pJobThread == NULL) { 41170 return; 41171 } 41172 41173 /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ 41174 { 41175 ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); 41176 ma_device_job_thread_post(pJobThread, &job); 41177 } 41178 41179 /* Wait for the thread to terminate naturally. */ 41180 if (pJobThread->_hasThread) { 41181 ma_thread_wait(&pJobThread->thread); 41182 } 41183 41184 /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ 41185 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); 41186 } 41187 41188 MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) 41189 { 41190 if (pJobThread == NULL || pJob == NULL) { 41191 return MA_INVALID_ARGS; 41192 } 41193 41194 return ma_job_queue_post(&pJobThread->jobQueue, pJob); 41195 } 41196 41197 MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) 41198 { 41199 if (pJob == NULL) { 41200 return MA_INVALID_ARGS; 41201 } 41202 41203 MA_ZERO_OBJECT(pJob); 41204 41205 if (pJobThread == NULL) { 41206 return MA_INVALID_ARGS; 41207 } 41208 41209 return ma_job_queue_next(&pJobThread->jobQueue, pJob); 41210 } 41211 41212 41213 41214 MA_API ma_context_config ma_context_config_init(void) 41215 { 41216 ma_context_config config; 41217 MA_ZERO_OBJECT(&config); 41218 41219 return config; 41220 } 41221 41222 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) 41223 { 41224 ma_result result; 41225 ma_context_config defaultConfig; 41226 ma_backend defaultBackends[ma_backend_null+1]; 41227 ma_uint32 iBackend; 41228 ma_backend* pBackendsToIterate; 41229 ma_uint32 backendsToIterateCount; 41230 41231 if (pContext == NULL) { 41232 return MA_INVALID_ARGS; 41233 } 41234 41235 MA_ZERO_OBJECT(pContext); 41236 41237 /* Always make sure the config is set first to ensure properties are available as soon as possible. */ 41238 if (pConfig == NULL) { 41239 defaultConfig = ma_context_config_init(); 41240 pConfig = &defaultConfig; 41241 } 41242 41243 /* Allocation callbacks need to come first because they'll be passed around to other areas. */ 41244 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); 41245 if (result != MA_SUCCESS) { 41246 return result; 41247 } 41248 41249 /* Get a lot set up first so we can start logging ASAP. */ 41250 if (pConfig->pLog != NULL) { 41251 pContext->pLog = pConfig->pLog; 41252 } else { 41253 result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); 41254 if (result == MA_SUCCESS) { 41255 pContext->pLog = &pContext->log; 41256 } else { 41257 pContext->pLog = NULL; /* Logging is not available. */ 41258 } 41259 } 41260 41261 pContext->threadPriority = pConfig->threadPriority; 41262 pContext->threadStackSize = pConfig->threadStackSize; 41263 pContext->pUserData = pConfig->pUserData; 41264 41265 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ 41266 result = ma_context_init_backend_apis(pContext); 41267 if (result != MA_SUCCESS) { 41268 return result; 41269 } 41270 41271 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { 41272 defaultBackends[iBackend] = (ma_backend)iBackend; 41273 } 41274 41275 pBackendsToIterate = (ma_backend*)backends; 41276 backendsToIterateCount = backendCount; 41277 if (pBackendsToIterate == NULL) { 41278 pBackendsToIterate = (ma_backend*)defaultBackends; 41279 backendsToIterateCount = ma_countof(defaultBackends); 41280 } 41281 41282 MA_ASSERT(pBackendsToIterate != NULL); 41283 41284 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { 41285 ma_backend backend = pBackendsToIterate[iBackend]; 41286 41287 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ 41288 MA_ZERO_OBJECT(&pContext->callbacks); 41289 41290 /* These backends are using the new callback system. */ 41291 switch (backend) { 41292 #ifdef MA_HAS_WASAPI 41293 case ma_backend_wasapi: 41294 { 41295 pContext->callbacks.onContextInit = ma_context_init__wasapi; 41296 } break; 41297 #endif 41298 #ifdef MA_HAS_DSOUND 41299 case ma_backend_dsound: 41300 { 41301 pContext->callbacks.onContextInit = ma_context_init__dsound; 41302 } break; 41303 #endif 41304 #ifdef MA_HAS_WINMM 41305 case ma_backend_winmm: 41306 { 41307 pContext->callbacks.onContextInit = ma_context_init__winmm; 41308 } break; 41309 #endif 41310 #ifdef MA_HAS_COREAUDIO 41311 case ma_backend_coreaudio: 41312 { 41313 pContext->callbacks.onContextInit = ma_context_init__coreaudio; 41314 } break; 41315 #endif 41316 #ifdef MA_HAS_SNDIO 41317 case ma_backend_sndio: 41318 { 41319 pContext->callbacks.onContextInit = ma_context_init__sndio; 41320 } break; 41321 #endif 41322 #ifdef MA_HAS_AUDIO4 41323 case ma_backend_audio4: 41324 { 41325 pContext->callbacks.onContextInit = ma_context_init__audio4; 41326 } break; 41327 #endif 41328 #ifdef MA_HAS_OSS 41329 case ma_backend_oss: 41330 { 41331 pContext->callbacks.onContextInit = ma_context_init__oss; 41332 } break; 41333 #endif 41334 #ifdef MA_HAS_PULSEAUDIO 41335 case ma_backend_pulseaudio: 41336 { 41337 pContext->callbacks.onContextInit = ma_context_init__pulse; 41338 } break; 41339 #endif 41340 #ifdef MA_HAS_ALSA 41341 case ma_backend_alsa: 41342 { 41343 pContext->callbacks.onContextInit = ma_context_init__alsa; 41344 } break; 41345 #endif 41346 #ifdef MA_HAS_JACK 41347 case ma_backend_jack: 41348 { 41349 pContext->callbacks.onContextInit = ma_context_init__jack; 41350 } break; 41351 #endif 41352 #ifdef MA_HAS_AAUDIO 41353 case ma_backend_aaudio: 41354 { 41355 if (ma_is_backend_enabled(backend)) { 41356 pContext->callbacks.onContextInit = ma_context_init__aaudio; 41357 } 41358 } break; 41359 #endif 41360 #ifdef MA_HAS_OPENSL 41361 case ma_backend_opensl: 41362 { 41363 if (ma_is_backend_enabled(backend)) { 41364 pContext->callbacks.onContextInit = ma_context_init__opensl; 41365 } 41366 } break; 41367 #endif 41368 #ifdef MA_HAS_WEBAUDIO 41369 case ma_backend_webaudio: 41370 { 41371 pContext->callbacks.onContextInit = ma_context_init__webaudio; 41372 } break; 41373 #endif 41374 #ifdef MA_HAS_CUSTOM 41375 case ma_backend_custom: 41376 { 41377 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ 41378 pContext->callbacks = pConfig->custom; 41379 } break; 41380 #endif 41381 #ifdef MA_HAS_NULL 41382 case ma_backend_null: 41383 { 41384 pContext->callbacks.onContextInit = ma_context_init__null; 41385 } break; 41386 #endif 41387 41388 default: break; 41389 } 41390 41391 if (pContext->callbacks.onContextInit != NULL) { 41392 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); 41393 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); 41394 } else { 41395 /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ 41396 if (backend != ma_backend_custom) { 41397 result = MA_BACKEND_NOT_ENABLED; 41398 } else { 41399 #if !defined(MA_HAS_CUSTOM) 41400 result = MA_BACKEND_NOT_ENABLED; 41401 #else 41402 result = MA_NO_BACKEND; 41403 #endif 41404 } 41405 } 41406 41407 /* If this iteration was successful, return. */ 41408 if (result == MA_SUCCESS) { 41409 result = ma_mutex_init(&pContext->deviceEnumLock); 41410 if (result != MA_SUCCESS) { 41411 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); 41412 } 41413 41414 result = ma_mutex_init(&pContext->deviceInfoLock); 41415 if (result != MA_SUCCESS) { 41416 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); 41417 } 41418 41419 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); 41420 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); 41421 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); 41422 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); 41423 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); 41424 41425 pContext->backend = backend; 41426 return result; 41427 } else { 41428 if (result == MA_BACKEND_NOT_ENABLED) { 41429 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); 41430 } else { 41431 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); 41432 } 41433 } 41434 } 41435 41436 /* If we get here it means an error occurred. */ 41437 MA_ZERO_OBJECT(pContext); /* Safety. */ 41438 return MA_NO_BACKEND; 41439 } 41440 41441 MA_API ma_result ma_context_uninit(ma_context* pContext) 41442 { 41443 if (pContext == NULL) { 41444 return MA_INVALID_ARGS; 41445 } 41446 41447 if (pContext->callbacks.onContextUninit != NULL) { 41448 pContext->callbacks.onContextUninit(pContext); 41449 } 41450 41451 ma_mutex_uninit(&pContext->deviceEnumLock); 41452 ma_mutex_uninit(&pContext->deviceInfoLock); 41453 ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); 41454 ma_context_uninit_backend_apis(pContext); 41455 41456 if (pContext->pLog == &pContext->log) { 41457 ma_log_uninit(&pContext->log); 41458 } 41459 41460 return MA_SUCCESS; 41461 } 41462 41463 MA_API size_t ma_context_sizeof(void) 41464 { 41465 return sizeof(ma_context); 41466 } 41467 41468 41469 MA_API ma_log* ma_context_get_log(ma_context* pContext) 41470 { 41471 if (pContext == NULL) { 41472 return NULL; 41473 } 41474 41475 return pContext->pLog; 41476 } 41477 41478 41479 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) 41480 { 41481 ma_result result; 41482 41483 if (pContext == NULL || callback == NULL) { 41484 return MA_INVALID_ARGS; 41485 } 41486 41487 if (pContext->callbacks.onContextEnumerateDevices == NULL) { 41488 return MA_INVALID_OPERATION; 41489 } 41490 41491 ma_mutex_lock(&pContext->deviceEnumLock); 41492 { 41493 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); 41494 } 41495 ma_mutex_unlock(&pContext->deviceEnumLock); 41496 41497 return result; 41498 } 41499 41500 41501 static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) 41502 { 41503 /* 41504 We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device 41505 it's just appended to the end. If it's a playback device it's inserted just before the first capture device. 41506 */ 41507 41508 /* 41509 First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a 41510 simple fixed size increment for buffer expansion. 41511 */ 41512 const ma_uint32 bufferExpansionCount = 2; 41513 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; 41514 41515 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { 41516 ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; 41517 ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); 41518 if (pNewInfos == NULL) { 41519 return MA_FALSE; /* Out of memory. */ 41520 } 41521 41522 pContext->pDeviceInfos = pNewInfos; 41523 pContext->deviceInfoCapacity = newCapacity; 41524 } 41525 41526 if (deviceType == ma_device_type_playback) { 41527 /* Playback. Insert just before the first capture device. */ 41528 41529 /* The first thing to do is move all of the capture devices down a slot. */ 41530 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; 41531 size_t iCaptureDevice; 41532 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { 41533 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; 41534 } 41535 41536 /* Now just insert where the first capture device was before moving it down a slot. */ 41537 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; 41538 pContext->playbackDeviceInfoCount += 1; 41539 } else { 41540 /* Capture. Insert at the end. */ 41541 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; 41542 pContext->captureDeviceInfoCount += 1; 41543 } 41544 41545 (void)pUserData; 41546 return MA_TRUE; 41547 } 41548 41549 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) 41550 { 41551 ma_result result; 41552 41553 /* Safety. */ 41554 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; 41555 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; 41556 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; 41557 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; 41558 41559 if (pContext == NULL) { 41560 return MA_INVALID_ARGS; 41561 } 41562 41563 if (pContext->callbacks.onContextEnumerateDevices == NULL) { 41564 return MA_INVALID_OPERATION; 41565 } 41566 41567 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ 41568 ma_mutex_lock(&pContext->deviceEnumLock); 41569 { 41570 /* Reset everything first. */ 41571 pContext->playbackDeviceInfoCount = 0; 41572 pContext->captureDeviceInfoCount = 0; 41573 41574 /* Now enumerate over available devices. */ 41575 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); 41576 if (result == MA_SUCCESS) { 41577 /* Playback devices. */ 41578 if (ppPlaybackDeviceInfos != NULL) { 41579 *ppPlaybackDeviceInfos = pContext->pDeviceInfos; 41580 } 41581 if (pPlaybackDeviceCount != NULL) { 41582 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; 41583 } 41584 41585 /* Capture devices. */ 41586 if (ppCaptureDeviceInfos != NULL) { 41587 *ppCaptureDeviceInfos = pContext->pDeviceInfos; 41588 /* Capture devices come after playback devices. */ 41589 if (pContext->playbackDeviceInfoCount > 0) { 41590 /* Conditional, because NULL+0 is undefined behavior. */ 41591 *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; 41592 } 41593 } 41594 if (pCaptureDeviceCount != NULL) { 41595 *pCaptureDeviceCount = pContext->captureDeviceInfoCount; 41596 } 41597 } 41598 } 41599 ma_mutex_unlock(&pContext->deviceEnumLock); 41600 41601 return result; 41602 } 41603 41604 MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) 41605 { 41606 ma_result result; 41607 ma_device_info deviceInfo; 41608 41609 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ 41610 if (pContext == NULL || pDeviceInfo == NULL) { 41611 return MA_INVALID_ARGS; 41612 } 41613 41614 MA_ZERO_OBJECT(&deviceInfo); 41615 41616 /* Help the backend out by copying over the device ID if we have one. */ 41617 if (pDeviceID != NULL) { 41618 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); 41619 } 41620 41621 if (pContext->callbacks.onContextGetDeviceInfo == NULL) { 41622 return MA_INVALID_OPERATION; 41623 } 41624 41625 ma_mutex_lock(&pContext->deviceInfoLock); 41626 { 41627 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); 41628 } 41629 ma_mutex_unlock(&pContext->deviceInfoLock); 41630 41631 *pDeviceInfo = deviceInfo; 41632 return result; 41633 } 41634 41635 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) 41636 { 41637 if (pContext == NULL) { 41638 return MA_FALSE; 41639 } 41640 41641 return ma_is_loopback_supported(pContext->backend); 41642 } 41643 41644 41645 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) 41646 { 41647 ma_device_config config; 41648 MA_ZERO_OBJECT(&config); 41649 config.deviceType = deviceType; 41650 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ 41651 41652 return config; 41653 } 41654 41655 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) 41656 { 41657 ma_result result; 41658 ma_device_descriptor descriptorPlayback; 41659 ma_device_descriptor descriptorCapture; 41660 41661 /* The context can be null, in which case we self-manage it. */ 41662 if (pContext == NULL) { 41663 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); 41664 } 41665 41666 if (pDevice == NULL) { 41667 return MA_INVALID_ARGS; 41668 } 41669 41670 MA_ZERO_OBJECT(pDevice); 41671 41672 if (pConfig == NULL) { 41673 return MA_INVALID_ARGS; 41674 } 41675 41676 /* Check that we have our callbacks defined. */ 41677 if (pContext->callbacks.onDeviceInit == NULL) { 41678 return MA_INVALID_OPERATION; 41679 } 41680 41681 /* Basic config validation. */ 41682 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { 41683 if (pConfig->capture.channels > MA_MAX_CHANNELS) { 41684 return MA_INVALID_ARGS; 41685 } 41686 41687 if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { 41688 return MA_INVALID_ARGS; 41689 } 41690 } 41691 41692 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { 41693 if (pConfig->playback.channels > MA_MAX_CHANNELS) { 41694 return MA_INVALID_ARGS; 41695 } 41696 41697 if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { 41698 return MA_INVALID_ARGS; 41699 } 41700 } 41701 41702 pDevice->pContext = pContext; 41703 41704 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ 41705 pDevice->pUserData = pConfig->pUserData; 41706 pDevice->onData = pConfig->dataCallback; 41707 pDevice->onNotification = pConfig->notificationCallback; 41708 pDevice->onStop = pConfig->stopCallback; 41709 41710 if (pConfig->playback.pDeviceID != NULL) { 41711 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); 41712 pDevice->playback.pID = &pDevice->playback.id; 41713 } else { 41714 pDevice->playback.pID = NULL; 41715 } 41716 41717 if (pConfig->capture.pDeviceID != NULL) { 41718 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); 41719 pDevice->capture.pID = &pDevice->capture.id; 41720 } else { 41721 pDevice->capture.pID = NULL; 41722 } 41723 41724 pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; 41725 pDevice->noClip = pConfig->noClip; 41726 pDevice->noDisableDenormals = pConfig->noDisableDenormals; 41727 pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; 41728 ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); 41729 41730 pDevice->type = pConfig->deviceType; 41731 pDevice->sampleRate = pConfig->sampleRate; 41732 pDevice->resampling.algorithm = pConfig->resampling.algorithm; 41733 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; 41734 pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; 41735 pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; 41736 41737 pDevice->capture.shareMode = pConfig->capture.shareMode; 41738 pDevice->capture.format = pConfig->capture.format; 41739 pDevice->capture.channels = pConfig->capture.channels; 41740 ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); 41741 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; 41742 pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; 41743 41744 pDevice->playback.shareMode = pConfig->playback.shareMode; 41745 pDevice->playback.format = pConfig->playback.format; 41746 pDevice->playback.channels = pConfig->playback.channels; 41747 ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); 41748 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; 41749 pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; 41750 41751 result = ma_mutex_init(&pDevice->startStopLock); 41752 if (result != MA_SUCCESS) { 41753 return result; 41754 } 41755 41756 /* 41757 When the device is started, the worker thread is the one that does the actual startup of the backend device. We 41758 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. 41759 41760 Each of these semaphores is released internally by the worker thread when the work is completed. The start 41761 semaphore is also used to wake up the worker thread. 41762 */ 41763 result = ma_event_init(&pDevice->wakeupEvent); 41764 if (result != MA_SUCCESS) { 41765 ma_mutex_uninit(&pDevice->startStopLock); 41766 return result; 41767 } 41768 41769 result = ma_event_init(&pDevice->startEvent); 41770 if (result != MA_SUCCESS) { 41771 ma_event_uninit(&pDevice->wakeupEvent); 41772 ma_mutex_uninit(&pDevice->startStopLock); 41773 return result; 41774 } 41775 41776 result = ma_event_init(&pDevice->stopEvent); 41777 if (result != MA_SUCCESS) { 41778 ma_event_uninit(&pDevice->startEvent); 41779 ma_event_uninit(&pDevice->wakeupEvent); 41780 ma_mutex_uninit(&pDevice->startStopLock); 41781 return result; 41782 } 41783 41784 41785 MA_ZERO_OBJECT(&descriptorPlayback); 41786 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; 41787 descriptorPlayback.shareMode = pConfig->playback.shareMode; 41788 descriptorPlayback.format = pConfig->playback.format; 41789 descriptorPlayback.channels = pConfig->playback.channels; 41790 descriptorPlayback.sampleRate = pConfig->sampleRate; 41791 ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); 41792 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; 41793 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; 41794 descriptorPlayback.periodCount = pConfig->periods; 41795 41796 if (descriptorPlayback.periodCount == 0) { 41797 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; 41798 } 41799 41800 41801 MA_ZERO_OBJECT(&descriptorCapture); 41802 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; 41803 descriptorCapture.shareMode = pConfig->capture.shareMode; 41804 descriptorCapture.format = pConfig->capture.format; 41805 descriptorCapture.channels = pConfig->capture.channels; 41806 descriptorCapture.sampleRate = pConfig->sampleRate; 41807 ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); 41808 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; 41809 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; 41810 descriptorCapture.periodCount = pConfig->periods; 41811 41812 if (descriptorCapture.periodCount == 0) { 41813 descriptorCapture.periodCount = MA_DEFAULT_PERIODS; 41814 } 41815 41816 41817 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); 41818 if (result != MA_SUCCESS) { 41819 ma_event_uninit(&pDevice->startEvent); 41820 ma_event_uninit(&pDevice->wakeupEvent); 41821 ma_mutex_uninit(&pDevice->startStopLock); 41822 return result; 41823 } 41824 41825 #if 0 41826 /* 41827 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between 41828 the requested format and the internal format. 41829 */ 41830 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { 41831 if (!ma_device_descriptor_is_valid(&descriptorCapture)) { 41832 ma_device_uninit(pDevice); 41833 return MA_INVALID_ARGS; 41834 } 41835 41836 pDevice->capture.internalFormat = descriptorCapture.format; 41837 pDevice->capture.internalChannels = descriptorCapture.channels; 41838 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; 41839 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); 41840 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; 41841 pDevice->capture.internalPeriods = descriptorCapture.periodCount; 41842 41843 if (pDevice->capture.internalPeriodSizeInFrames == 0) { 41844 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); 41845 } 41846 } 41847 41848 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 41849 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { 41850 ma_device_uninit(pDevice); 41851 return MA_INVALID_ARGS; 41852 } 41853 41854 pDevice->playback.internalFormat = descriptorPlayback.format; 41855 pDevice->playback.internalChannels = descriptorPlayback.channels; 41856 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; 41857 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); 41858 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; 41859 pDevice->playback.internalPeriods = descriptorPlayback.periodCount; 41860 41861 if (pDevice->playback.internalPeriodSizeInFrames == 0) { 41862 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); 41863 } 41864 } 41865 41866 41867 /* 41868 The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. 41869 For loopback devices, we need to retrieve the name of the playback device. 41870 */ 41871 { 41872 ma_device_info deviceInfo; 41873 41874 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { 41875 result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); 41876 if (result == MA_SUCCESS) { 41877 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); 41878 } else { 41879 /* We failed to retrieve the device info. Fall back to a default name. */ 41880 if (descriptorCapture.pDeviceID == NULL) { 41881 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); 41882 } else { 41883 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); 41884 } 41885 } 41886 } 41887 41888 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 41889 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); 41890 if (result == MA_SUCCESS) { 41891 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); 41892 } else { 41893 /* We failed to retrieve the device info. Fall back to a default name. */ 41894 if (descriptorPlayback.pDeviceID == NULL) { 41895 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); 41896 } else { 41897 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); 41898 } 41899 } 41900 } 41901 } 41902 41903 41904 ma_device__post_init_setup(pDevice, pConfig->deviceType); 41905 #endif 41906 41907 result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); 41908 if (result != MA_SUCCESS) { 41909 ma_device_uninit(pDevice); 41910 return result; 41911 } 41912 41913 41914 /* 41915 If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to 41916 be done after post_init_setup() because we'll need access to the sample rate. 41917 */ 41918 if (pConfig->noFixedSizedCallback == MA_FALSE) { 41919 /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ 41920 ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; 41921 if (intermediaryBufferCap == 0) { 41922 intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); 41923 } 41924 41925 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { 41926 ma_uint32 intermediaryBufferSizeInBytes; 41927 41928 pDevice->capture.intermediaryBufferLen = 0; 41929 pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; 41930 if (pDevice->capture.intermediaryBufferCap == 0) { 41931 pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; 41932 } 41933 41934 intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); 41935 41936 pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); 41937 if (pDevice->capture.pIntermediaryBuffer == NULL) { 41938 ma_device_uninit(pDevice); 41939 return MA_OUT_OF_MEMORY; 41940 } 41941 41942 /* Silence the buffer for safety. */ 41943 ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); 41944 pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; 41945 } 41946 41947 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { 41948 ma_uint64 intermediaryBufferSizeInBytes; 41949 41950 pDevice->playback.intermediaryBufferLen = 0; 41951 if (pConfig->deviceType == ma_device_type_duplex) { 41952 pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ 41953 } else { 41954 pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; 41955 if (pDevice->playback.intermediaryBufferCap == 0) { 41956 pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; 41957 } 41958 } 41959 41960 intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); 41961 41962 pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); 41963 if (pDevice->playback.pIntermediaryBuffer == NULL) { 41964 ma_device_uninit(pDevice); 41965 return MA_OUT_OF_MEMORY; 41966 } 41967 41968 /* Silence the buffer for safety. */ 41969 ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); 41970 pDevice->playback.intermediaryBufferLen = 0; 41971 } 41972 } else { 41973 /* Not using a fixed sized data callback so no need for an intermediary buffer. */ 41974 } 41975 41976 41977 /* Some backends don't require the worker thread. */ 41978 if (!ma_context_is_backend_asynchronous(pContext)) { 41979 /* The worker thread. */ 41980 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); 41981 if (result != MA_SUCCESS) { 41982 ma_device_uninit(pDevice); 41983 return result; 41984 } 41985 41986 /* Wait for the worker thread to put the device into it's stopped state for real. */ 41987 ma_event_wait(&pDevice->stopEvent); 41988 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); 41989 } else { 41990 /* 41991 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done 41992 after ma_device__post_init_setup(). 41993 */ 41994 if (ma_context_is_backend_asynchronous(pContext)) { 41995 if (pConfig->deviceType == ma_device_type_duplex) { 41996 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); 41997 if (result != MA_SUCCESS) { 41998 ma_device_uninit(pDevice); 41999 return result; 42000 } 42001 } 42002 } 42003 42004 ma_device__set_state(pDevice, ma_device_state_stopped); 42005 } 42006 42007 /* Log device information. */ 42008 { 42009 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); 42010 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 42011 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; 42012 ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); 42013 42014 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); 42015 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); 42016 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); 42017 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); 42018 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); 42019 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); 42020 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); 42021 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); 42022 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); 42023 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); 42024 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); 42025 { 42026 char channelMapStr[1024]; 42027 ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); 42028 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); 42029 42030 ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); 42031 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); 42032 } 42033 } 42034 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 42035 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; 42036 ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); 42037 42038 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); 42039 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); 42040 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); 42041 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); 42042 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); 42043 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); 42044 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); 42045 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); 42046 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); 42047 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); 42048 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); 42049 { 42050 char channelMapStr[1024]; 42051 ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); 42052 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); 42053 42054 ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); 42055 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); 42056 } 42057 } 42058 } 42059 42060 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); 42061 return MA_SUCCESS; 42062 } 42063 42064 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) 42065 { 42066 ma_result result; 42067 ma_context* pContext; 42068 ma_backend defaultBackends[ma_backend_null+1]; 42069 ma_uint32 iBackend; 42070 ma_backend* pBackendsToIterate; 42071 ma_uint32 backendsToIterateCount; 42072 ma_allocation_callbacks allocationCallbacks; 42073 42074 if (pConfig == NULL) { 42075 return MA_INVALID_ARGS; 42076 } 42077 42078 if (pContextConfig != NULL) { 42079 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); 42080 if (result != MA_SUCCESS) { 42081 return result; 42082 } 42083 } else { 42084 allocationCallbacks = ma_allocation_callbacks_init_default(); 42085 } 42086 42087 pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); 42088 if (pContext == NULL) { 42089 return MA_OUT_OF_MEMORY; 42090 } 42091 42092 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { 42093 defaultBackends[iBackend] = (ma_backend)iBackend; 42094 } 42095 42096 pBackendsToIterate = (ma_backend*)backends; 42097 backendsToIterateCount = backendCount; 42098 if (pBackendsToIterate == NULL) { 42099 pBackendsToIterate = (ma_backend*)defaultBackends; 42100 backendsToIterateCount = ma_countof(defaultBackends); 42101 } 42102 42103 result = MA_NO_BACKEND; 42104 42105 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { 42106 /* 42107 This is a hack for iOS. If the context config is null, there's a good chance the 42108 `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this 42109 case, set the session category based on the device type. 42110 */ 42111 #if defined(MA_APPLE_MOBILE) 42112 ma_context_config contextConfig; 42113 42114 if (pContextConfig == NULL) { 42115 contextConfig = ma_context_config_init(); 42116 switch (pConfig->deviceType) { 42117 case ma_device_type_duplex: { 42118 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; 42119 } break; 42120 case ma_device_type_capture: { 42121 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; 42122 } break; 42123 case ma_device_type_playback: 42124 default: { 42125 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; 42126 } break; 42127 } 42128 42129 pContextConfig = &contextConfig; 42130 } 42131 #endif 42132 42133 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); 42134 if (result == MA_SUCCESS) { 42135 result = ma_device_init(pContext, pConfig, pDevice); 42136 if (result == MA_SUCCESS) { 42137 break; /* Success. */ 42138 } else { 42139 ma_context_uninit(pContext); /* Failure. */ 42140 } 42141 } 42142 } 42143 42144 if (result != MA_SUCCESS) { 42145 ma_free(pContext, &allocationCallbacks); 42146 return result; 42147 } 42148 42149 pDevice->isOwnerOfContext = MA_TRUE; 42150 return result; 42151 } 42152 42153 MA_API void ma_device_uninit(ma_device* pDevice) 42154 { 42155 if (!ma_device__is_initialized(pDevice)) { 42156 return; 42157 } 42158 42159 /* 42160 It's possible for the miniaudio side of the device and the backend to not be in sync due to 42161 system-level situations such as the computer being put into sleep mode and the backend not 42162 notifying miniaudio of the fact the device has stopped. It's possible for this to result in a 42163 deadlock due to miniaudio thinking the device is in a running state, when in fact it's not 42164 running at all. For this reason I am no longer explicitly stopping the device. I don't think 42165 this should affect anyone in practice since uninitializing the backend will naturally stop the 42166 device anyway. 42167 */ 42168 #if 0 42169 { 42170 /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ 42171 if (ma_device_is_started(pDevice)) { 42172 ma_device_stop(pDevice); 42173 } 42174 } 42175 #endif 42176 42177 /* Putting the device into an uninitialized state will make the worker thread return. */ 42178 ma_device__set_state(pDevice, ma_device_state_uninitialized); 42179 42180 /* Wake up the worker thread and wait for it to properly terminate. */ 42181 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { 42182 ma_event_signal(&pDevice->wakeupEvent); 42183 ma_thread_wait(&pDevice->thread); 42184 } 42185 42186 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { 42187 pDevice->pContext->callbacks.onDeviceUninit(pDevice); 42188 } 42189 42190 42191 ma_event_uninit(&pDevice->stopEvent); 42192 ma_event_uninit(&pDevice->startEvent); 42193 ma_event_uninit(&pDevice->wakeupEvent); 42194 ma_mutex_uninit(&pDevice->startStopLock); 42195 42196 if (ma_context_is_backend_asynchronous(pDevice->pContext)) { 42197 if (pDevice->type == ma_device_type_duplex) { 42198 ma_duplex_rb_uninit(&pDevice->duplexRB); 42199 } 42200 } 42201 42202 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { 42203 ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); 42204 } 42205 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { 42206 ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); 42207 } 42208 42209 if (pDevice->playback.pInputCache != NULL) { 42210 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); 42211 } 42212 42213 if (pDevice->capture.pIntermediaryBuffer != NULL) { 42214 ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); 42215 } 42216 if (pDevice->playback.pIntermediaryBuffer != NULL) { 42217 ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); 42218 } 42219 42220 if (pDevice->isOwnerOfContext) { 42221 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; 42222 42223 ma_context_uninit(pDevice->pContext); 42224 ma_free(pDevice->pContext, &allocationCallbacks); 42225 } 42226 42227 MA_ZERO_OBJECT(pDevice); 42228 } 42229 42230 MA_API ma_context* ma_device_get_context(ma_device* pDevice) 42231 { 42232 if (pDevice == NULL) { 42233 return NULL; 42234 } 42235 42236 return pDevice->pContext; 42237 } 42238 42239 MA_API ma_log* ma_device_get_log(ma_device* pDevice) 42240 { 42241 return ma_context_get_log(ma_device_get_context(pDevice)); 42242 } 42243 42244 MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) 42245 { 42246 if (pDeviceInfo == NULL) { 42247 return MA_INVALID_ARGS; 42248 } 42249 42250 MA_ZERO_OBJECT(pDeviceInfo); 42251 42252 if (pDevice == NULL) { 42253 return MA_INVALID_ARGS; 42254 } 42255 42256 /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ 42257 if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { 42258 return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); 42259 } 42260 42261 /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ 42262 if (type == ma_device_type_playback) { 42263 return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); 42264 } else { 42265 return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); 42266 } 42267 } 42268 42269 MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) 42270 { 42271 ma_result result; 42272 ma_device_info deviceInfo; 42273 42274 if (pLengthNotIncludingNullTerminator != NULL) { 42275 *pLengthNotIncludingNullTerminator = 0; 42276 } 42277 42278 if (pName != NULL && nameCap > 0) { 42279 pName[0] = '\0'; 42280 } 42281 42282 result = ma_device_get_info(pDevice, type, &deviceInfo); 42283 if (result != MA_SUCCESS) { 42284 return result; 42285 } 42286 42287 if (pName != NULL) { 42288 ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); 42289 42290 /* 42291 For safety, make sure the length is based on the truncated output string rather than the 42292 source. Otherwise the caller might assume the output buffer contains more content than it 42293 actually does. 42294 */ 42295 if (pLengthNotIncludingNullTerminator != NULL) { 42296 *pLengthNotIncludingNullTerminator = strlen(pName); 42297 } 42298 } else { 42299 /* Name not specified. Just report the length of the source string. */ 42300 if (pLengthNotIncludingNullTerminator != NULL) { 42301 *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); 42302 } 42303 } 42304 42305 return MA_SUCCESS; 42306 } 42307 42308 MA_API ma_result ma_device_start(ma_device* pDevice) 42309 { 42310 ma_result result; 42311 42312 if (pDevice == NULL) { 42313 return MA_INVALID_ARGS; 42314 } 42315 42316 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { 42317 return MA_INVALID_OPERATION; /* Not initialized. */ 42318 } 42319 42320 if (ma_device_get_state(pDevice) == ma_device_state_started) { 42321 return MA_SUCCESS; /* Already started. */ 42322 } 42323 42324 ma_mutex_lock(&pDevice->startStopLock); 42325 { 42326 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ 42327 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); 42328 42329 ma_device__set_state(pDevice, ma_device_state_starting); 42330 42331 /* Asynchronous backends need to be handled differently. */ 42332 if (ma_context_is_backend_asynchronous(pDevice->pContext)) { 42333 if (pDevice->pContext->callbacks.onDeviceStart != NULL) { 42334 result = pDevice->pContext->callbacks.onDeviceStart(pDevice); 42335 } else { 42336 result = MA_INVALID_OPERATION; 42337 } 42338 42339 if (result == MA_SUCCESS) { 42340 ma_device__set_state(pDevice, ma_device_state_started); 42341 ma_device__on_notification_started(pDevice); 42342 } 42343 } else { 42344 /* 42345 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the 42346 thread and then wait for the start event. 42347 */ 42348 ma_event_signal(&pDevice->wakeupEvent); 42349 42350 /* 42351 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device 42352 into the started state. Don't call ma_device__set_state() here. 42353 */ 42354 ma_event_wait(&pDevice->startEvent); 42355 result = pDevice->workResult; 42356 } 42357 42358 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ 42359 if (result != MA_SUCCESS) { 42360 ma_device__set_state(pDevice, ma_device_state_stopped); 42361 } 42362 } 42363 ma_mutex_unlock(&pDevice->startStopLock); 42364 42365 return result; 42366 } 42367 42368 MA_API ma_result ma_device_stop(ma_device* pDevice) 42369 { 42370 ma_result result; 42371 42372 if (pDevice == NULL) { 42373 return MA_INVALID_ARGS; 42374 } 42375 42376 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { 42377 return MA_INVALID_OPERATION; /* Not initialized. */ 42378 } 42379 42380 if (ma_device_get_state(pDevice) == ma_device_state_stopped) { 42381 return MA_SUCCESS; /* Already stopped. */ 42382 } 42383 42384 ma_mutex_lock(&pDevice->startStopLock); 42385 { 42386 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ 42387 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); 42388 42389 ma_device__set_state(pDevice, ma_device_state_stopping); 42390 42391 /* Asynchronous backends need to be handled differently. */ 42392 if (ma_context_is_backend_asynchronous(pDevice->pContext)) { 42393 /* Asynchronous backends must have a stop operation. */ 42394 if (pDevice->pContext->callbacks.onDeviceStop != NULL) { 42395 result = pDevice->pContext->callbacks.onDeviceStop(pDevice); 42396 } else { 42397 result = MA_INVALID_OPERATION; 42398 } 42399 42400 ma_device__set_state(pDevice, ma_device_state_stopped); 42401 } else { 42402 /* 42403 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If 42404 the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make 42405 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super 42406 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. 42407 */ 42408 MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); 42409 42410 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { 42411 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); 42412 } 42413 42414 /* 42415 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be 42416 the one who puts the device into the stopped state. Don't call ma_device__set_state() here. 42417 */ 42418 ma_event_wait(&pDevice->stopEvent); 42419 result = MA_SUCCESS; 42420 } 42421 42422 /* 42423 This is a safety measure to ensure the internal buffer has been cleared so any leftover 42424 does not get played the next time the device starts. Ideally this should be drained by 42425 the backend first. 42426 */ 42427 pDevice->playback.intermediaryBufferLen = 0; 42428 pDevice->playback.inputCacheConsumed = 0; 42429 pDevice->playback.inputCacheRemaining = 0; 42430 } 42431 ma_mutex_unlock(&pDevice->startStopLock); 42432 42433 return result; 42434 } 42435 42436 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) 42437 { 42438 return ma_device_get_state(pDevice) == ma_device_state_started; 42439 } 42440 42441 MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) 42442 { 42443 if (pDevice == NULL) { 42444 return ma_device_state_uninitialized; 42445 } 42446 42447 return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ 42448 } 42449 42450 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) 42451 { 42452 if (pDevice == NULL) { 42453 return MA_INVALID_ARGS; 42454 } 42455 42456 if (volume < 0.0f) { 42457 return MA_INVALID_ARGS; 42458 } 42459 42460 ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); 42461 42462 return MA_SUCCESS; 42463 } 42464 42465 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) 42466 { 42467 if (pVolume == NULL) { 42468 return MA_INVALID_ARGS; 42469 } 42470 42471 if (pDevice == NULL) { 42472 *pVolume = 0; 42473 return MA_INVALID_ARGS; 42474 } 42475 42476 *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); 42477 42478 return MA_SUCCESS; 42479 } 42480 42481 MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) 42482 { 42483 if (gainDB > 0) { 42484 return MA_INVALID_ARGS; 42485 } 42486 42487 return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); 42488 } 42489 42490 MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) 42491 { 42492 float factor; 42493 ma_result result; 42494 42495 if (pGainDB == NULL) { 42496 return MA_INVALID_ARGS; 42497 } 42498 42499 result = ma_device_get_master_volume(pDevice, &factor); 42500 if (result != MA_SUCCESS) { 42501 *pGainDB = 0; 42502 return result; 42503 } 42504 42505 *pGainDB = ma_volume_linear_to_db(factor); 42506 42507 return MA_SUCCESS; 42508 } 42509 42510 42511 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) 42512 { 42513 if (pDevice == NULL) { 42514 return MA_INVALID_ARGS; 42515 } 42516 42517 if (pOutput == NULL && pInput == NULL) { 42518 return MA_INVALID_ARGS; 42519 } 42520 42521 if (pDevice->type == ma_device_type_duplex) { 42522 if (pInput != NULL) { 42523 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); 42524 } 42525 42526 if (pOutput != NULL) { 42527 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); 42528 } 42529 } else { 42530 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { 42531 if (pInput == NULL) { 42532 return MA_INVALID_ARGS; 42533 } 42534 42535 ma_device__send_frames_to_client(pDevice, frameCount, pInput); 42536 } 42537 42538 if (pDevice->type == ma_device_type_playback) { 42539 if (pOutput == NULL) { 42540 return MA_INVALID_ARGS; 42541 } 42542 42543 ma_device__read_frames_from_client(pDevice, frameCount, pOutput); 42544 } 42545 } 42546 42547 return MA_SUCCESS; 42548 } 42549 42550 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) 42551 { 42552 if (pDescriptor == NULL) { 42553 return 0; 42554 } 42555 42556 /* 42557 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the 42558 time when the size of the buffer needs to be determined. In this case we need to just take a best 42559 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll 42560 just fall back to MA_DEFAULT_SAMPLE_RATE. 42561 */ 42562 if (nativeSampleRate == 0) { 42563 nativeSampleRate = pDescriptor->sampleRate; 42564 } 42565 if (nativeSampleRate == 0) { 42566 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; 42567 } 42568 42569 MA_ASSERT(nativeSampleRate != 0); 42570 42571 if (pDescriptor->periodSizeInFrames == 0) { 42572 if (pDescriptor->periodSizeInMilliseconds == 0) { 42573 if (performanceProfile == ma_performance_profile_low_latency) { 42574 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); 42575 } else { 42576 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); 42577 } 42578 } else { 42579 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); 42580 } 42581 } else { 42582 return pDescriptor->periodSizeInFrames; 42583 } 42584 } 42585 #endif /* MA_NO_DEVICE_IO */ 42586 42587 42588 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) 42589 { 42590 /* Prevent a division by zero. */ 42591 if (sampleRate == 0) { 42592 return 0; 42593 } 42594 42595 return bufferSizeInFrames*1000 / sampleRate; 42596 } 42597 42598 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) 42599 { 42600 /* Prevent a division by zero. */ 42601 if (sampleRate == 0) { 42602 return 0; 42603 } 42604 42605 return bufferSizeInMilliseconds*sampleRate / 1000; 42606 } 42607 42608 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) 42609 { 42610 if (dst == src) { 42611 return; /* No-op. */ 42612 } 42613 42614 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); 42615 } 42616 42617 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) 42618 { 42619 if (format == ma_format_u8) { 42620 ma_uint64 sampleCount = frameCount * channels; 42621 ma_uint64 iSample; 42622 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42623 ((ma_uint8*)p)[iSample] = 128; 42624 } 42625 } else { 42626 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); 42627 } 42628 } 42629 42630 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) 42631 { 42632 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); 42633 } 42634 42635 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) 42636 { 42637 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); 42638 } 42639 42640 42641 MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) 42642 { 42643 ma_uint64 iSample; 42644 42645 MA_ASSERT(pDst != NULL); 42646 MA_ASSERT(pSrc != NULL); 42647 42648 for (iSample = 0; iSample < count; iSample += 1) { 42649 pDst[iSample] = ma_clip_u8(pSrc[iSample]); 42650 } 42651 } 42652 42653 MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) 42654 { 42655 ma_uint64 iSample; 42656 42657 MA_ASSERT(pDst != NULL); 42658 MA_ASSERT(pSrc != NULL); 42659 42660 for (iSample = 0; iSample < count; iSample += 1) { 42661 pDst[iSample] = ma_clip_s16(pSrc[iSample]); 42662 } 42663 } 42664 42665 MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) 42666 { 42667 ma_uint64 iSample; 42668 42669 MA_ASSERT(pDst != NULL); 42670 MA_ASSERT(pSrc != NULL); 42671 42672 for (iSample = 0; iSample < count; iSample += 1) { 42673 ma_int64 s = ma_clip_s24(pSrc[iSample]); 42674 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); 42675 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); 42676 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); 42677 } 42678 } 42679 42680 MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) 42681 { 42682 ma_uint64 iSample; 42683 42684 MA_ASSERT(pDst != NULL); 42685 MA_ASSERT(pSrc != NULL); 42686 42687 for (iSample = 0; iSample < count; iSample += 1) { 42688 pDst[iSample] = ma_clip_s32(pSrc[iSample]); 42689 } 42690 } 42691 42692 MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) 42693 { 42694 ma_uint64 iSample; 42695 42696 MA_ASSERT(pDst != NULL); 42697 MA_ASSERT(pSrc != NULL); 42698 42699 for (iSample = 0; iSample < count; iSample += 1) { 42700 pDst[iSample] = ma_clip_f32(pSrc[iSample]); 42701 } 42702 } 42703 42704 MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) 42705 { 42706 ma_uint64 sampleCount; 42707 42708 MA_ASSERT(pDst != NULL); 42709 MA_ASSERT(pSrc != NULL); 42710 42711 sampleCount = frameCount * channels; 42712 42713 switch (format) { 42714 case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; 42715 case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; 42716 case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; 42717 case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; 42718 case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; 42719 42720 /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ 42721 case ma_format_unknown: 42722 case ma_format_count: 42723 break; 42724 } 42725 } 42726 42727 42728 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) 42729 { 42730 ma_uint64 iSample; 42731 42732 if (pSamplesOut == NULL || pSamplesIn == NULL) { 42733 return; 42734 } 42735 42736 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42737 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); 42738 } 42739 } 42740 42741 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) 42742 { 42743 ma_uint64 iSample; 42744 42745 if (pSamplesOut == NULL || pSamplesIn == NULL) { 42746 return; 42747 } 42748 42749 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42750 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); 42751 } 42752 } 42753 42754 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) 42755 { 42756 ma_uint64 iSample; 42757 ma_uint8* pSamplesOut8; 42758 ma_uint8* pSamplesIn8; 42759 42760 if (pSamplesOut == NULL || pSamplesIn == NULL) { 42761 return; 42762 } 42763 42764 pSamplesOut8 = (ma_uint8*)pSamplesOut; 42765 pSamplesIn8 = (ma_uint8*)pSamplesIn; 42766 42767 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42768 ma_int32 sampleS32; 42769 42770 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); 42771 sampleS32 = (ma_int32)(sampleS32 * factor); 42772 42773 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); 42774 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); 42775 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); 42776 } 42777 } 42778 42779 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) 42780 { 42781 ma_uint64 iSample; 42782 42783 if (pSamplesOut == NULL || pSamplesIn == NULL) { 42784 return; 42785 } 42786 42787 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42788 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); 42789 } 42790 } 42791 42792 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) 42793 { 42794 ma_uint64 iSample; 42795 42796 if (pSamplesOut == NULL || pSamplesIn == NULL) { 42797 return; 42798 } 42799 42800 if (factor == 1) { 42801 if (pSamplesOut == pSamplesIn) { 42802 /* In place. No-op. */ 42803 } else { 42804 /* Just a copy. */ 42805 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42806 pSamplesOut[iSample] = pSamplesIn[iSample]; 42807 } 42808 } 42809 } else { 42810 for (iSample = 0; iSample < sampleCount; iSample += 1) { 42811 pSamplesOut[iSample] = pSamplesIn[iSample] * factor; 42812 } 42813 } 42814 } 42815 42816 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) 42817 { 42818 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); 42819 } 42820 42821 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) 42822 { 42823 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); 42824 } 42825 42826 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) 42827 { 42828 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); 42829 } 42830 42831 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) 42832 { 42833 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); 42834 } 42835 42836 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) 42837 { 42838 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); 42839 } 42840 42841 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) 42842 { 42843 ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); 42844 } 42845 42846 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) 42847 { 42848 ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); 42849 } 42850 42851 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) 42852 { 42853 ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); 42854 } 42855 42856 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) 42857 { 42858 ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); 42859 } 42860 42861 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) 42862 { 42863 ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); 42864 } 42865 42866 MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) 42867 { 42868 switch (format) 42869 { 42870 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; 42871 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; 42872 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; 42873 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; 42874 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; 42875 default: return; /* Do nothing. */ 42876 } 42877 } 42878 42879 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) 42880 { 42881 ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); 42882 } 42883 42884 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) 42885 { 42886 ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); 42887 } 42888 42889 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) 42890 { 42891 ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); 42892 } 42893 42894 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) 42895 { 42896 ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); 42897 } 42898 42899 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) 42900 { 42901 ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); 42902 } 42903 42904 MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) 42905 { 42906 ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); 42907 } 42908 42909 42910 MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) 42911 { 42912 ma_uint64 iFrame; 42913 42914 if (channels == 2) { 42915 /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ 42916 } 42917 42918 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 42919 ma_uint32 iChannel; 42920 for (iChannel = 0; iChannel < channels; iChannel += 1) { 42921 pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; 42922 } 42923 } 42924 } 42925 42926 42927 42928 static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) 42929 { 42930 return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); 42931 } 42932 42933 static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) 42934 { 42935 return (ma_int32)((x * volume) >> 8); 42936 } 42937 42938 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) 42939 { 42940 return (ma_int64)((x * volume) >> 8); 42941 } 42942 42943 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) 42944 { 42945 return (ma_int64)((x * volume) >> 8); 42946 } 42947 42948 static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) 42949 { 42950 return x * volume; 42951 } 42952 42953 42954 MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) 42955 { 42956 ma_uint64 iSample; 42957 ma_int16 volumeFixed; 42958 42959 MA_ASSERT(pDst != NULL); 42960 MA_ASSERT(pSrc != NULL); 42961 42962 volumeFixed = ma_float_to_fixed_16(volume); 42963 42964 for (iSample = 0; iSample < count; iSample += 1) { 42965 pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); 42966 } 42967 } 42968 42969 MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) 42970 { 42971 ma_uint64 iSample; 42972 ma_int16 volumeFixed; 42973 42974 MA_ASSERT(pDst != NULL); 42975 MA_ASSERT(pSrc != NULL); 42976 42977 volumeFixed = ma_float_to_fixed_16(volume); 42978 42979 for (iSample = 0; iSample < count; iSample += 1) { 42980 pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); 42981 } 42982 } 42983 42984 MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) 42985 { 42986 ma_uint64 iSample; 42987 ma_int16 volumeFixed; 42988 42989 MA_ASSERT(pDst != NULL); 42990 MA_ASSERT(pSrc != NULL); 42991 42992 volumeFixed = ma_float_to_fixed_16(volume); 42993 42994 for (iSample = 0; iSample < count; iSample += 1) { 42995 ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); 42996 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); 42997 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); 42998 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); 42999 } 43000 } 43001 43002 MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) 43003 { 43004 ma_uint64 iSample; 43005 ma_int16 volumeFixed; 43006 43007 MA_ASSERT(pDst != NULL); 43008 MA_ASSERT(pSrc != NULL); 43009 43010 volumeFixed = ma_float_to_fixed_16(volume); 43011 43012 for (iSample = 0; iSample < count; iSample += 1) { 43013 pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); 43014 } 43015 } 43016 43017 MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) 43018 { 43019 ma_uint64 iSample; 43020 43021 MA_ASSERT(pDst != NULL); 43022 MA_ASSERT(pSrc != NULL); 43023 43024 /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ 43025 43026 for (iSample = 0; iSample < count; iSample += 1) { 43027 pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); 43028 } 43029 } 43030 43031 MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) 43032 { 43033 MA_ASSERT(pDst != NULL); 43034 MA_ASSERT(pSrc != NULL); 43035 43036 if (volume == 1) { 43037 ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ 43038 } else if (volume == 0) { 43039 ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ 43040 } else { 43041 ma_uint64 sampleCount = frameCount * channels; 43042 43043 switch (format) { 43044 case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; 43045 case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; 43046 case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; 43047 case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; 43048 case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; 43049 43050 /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ 43051 case ma_format_unknown: 43052 case ma_format_count: 43053 break; 43054 } 43055 } 43056 } 43057 43058 43059 43060 MA_API float ma_volume_linear_to_db(float factor) 43061 { 43062 return 20*ma_log10f(factor); 43063 } 43064 43065 MA_API float ma_volume_db_to_linear(float gain) 43066 { 43067 return ma_powf(10, gain/20.0f); 43068 } 43069 43070 43071 MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) 43072 { 43073 ma_uint64 iSample; 43074 ma_uint64 sampleCount; 43075 43076 if (pDst == NULL || pSrc == NULL || channels == 0) { 43077 return MA_INVALID_ARGS; 43078 } 43079 43080 if (volume == 0) { 43081 return MA_SUCCESS; /* No changes if the volume is 0. */ 43082 } 43083 43084 sampleCount = frameCount * channels; 43085 43086 if (volume == 1) { 43087 for (iSample = 0; iSample < sampleCount; iSample += 1) { 43088 pDst[iSample] += pSrc[iSample]; 43089 } 43090 } else { 43091 for (iSample = 0; iSample < sampleCount; iSample += 1) { 43092 pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); 43093 } 43094 } 43095 43096 return MA_SUCCESS; 43097 } 43098 43099 43100 43101 /************************************************************************************************************************************************************** 43102 43103 Format Conversion 43104 43105 **************************************************************************************************************************************************************/ 43106 43107 static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) 43108 { 43109 return (ma_int16)(x * 32767.0f); 43110 } 43111 43112 static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) 43113 { 43114 return (ma_int16)((ma_int16)x - 128); 43115 } 43116 43117 static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) 43118 { 43119 return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ 43120 } 43121 43122 static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) 43123 { 43124 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); 43125 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); 43126 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); 43127 } 43128 43129 43130 /* u8 */ 43131 MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43132 { 43133 (void)ditherMode; 43134 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); 43135 } 43136 43137 43138 static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43139 { 43140 ma_int16* dst_s16 = (ma_int16*)dst; 43141 const ma_uint8* src_u8 = (const ma_uint8*)src; 43142 43143 ma_uint64 i; 43144 for (i = 0; i < count; i += 1) { 43145 ma_int16 x = src_u8[i]; 43146 x = (ma_int16)(x - 128); 43147 x = (ma_int16)(x << 8); 43148 dst_s16[i] = x; 43149 } 43150 43151 (void)ditherMode; 43152 } 43153 43154 static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43155 { 43156 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); 43157 } 43158 43159 #if defined(MA_SUPPORT_SSE2) 43160 static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43161 { 43162 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); 43163 } 43164 #endif 43165 #if defined(MA_SUPPORT_NEON) 43166 static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43167 { 43168 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); 43169 } 43170 #endif 43171 43172 MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43173 { 43174 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43175 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); 43176 #else 43177 # if defined(MA_SUPPORT_SSE2) 43178 if (ma_has_sse2()) { 43179 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); 43180 } else 43181 #elif defined(MA_SUPPORT_NEON) 43182 if (ma_has_neon()) { 43183 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); 43184 } else 43185 #endif 43186 { 43187 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); 43188 } 43189 #endif 43190 } 43191 43192 43193 static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43194 { 43195 ma_uint8* dst_s24 = (ma_uint8*)dst; 43196 const ma_uint8* src_u8 = (const ma_uint8*)src; 43197 43198 ma_uint64 i; 43199 for (i = 0; i < count; i += 1) { 43200 ma_int16 x = src_u8[i]; 43201 x = (ma_int16)(x - 128); 43202 43203 dst_s24[i*3+0] = 0; 43204 dst_s24[i*3+1] = 0; 43205 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); 43206 } 43207 43208 (void)ditherMode; 43209 } 43210 43211 static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43212 { 43213 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); 43214 } 43215 43216 #if defined(MA_SUPPORT_SSE2) 43217 static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43218 { 43219 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); 43220 } 43221 #endif 43222 #if defined(MA_SUPPORT_NEON) 43223 static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43224 { 43225 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); 43226 } 43227 #endif 43228 43229 MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43230 { 43231 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43232 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); 43233 #else 43234 # if defined(MA_SUPPORT_SSE2) 43235 if (ma_has_sse2()) { 43236 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); 43237 } else 43238 #elif defined(MA_SUPPORT_NEON) 43239 if (ma_has_neon()) { 43240 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); 43241 } else 43242 #endif 43243 { 43244 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); 43245 } 43246 #endif 43247 } 43248 43249 43250 static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43251 { 43252 ma_int32* dst_s32 = (ma_int32*)dst; 43253 const ma_uint8* src_u8 = (const ma_uint8*)src; 43254 43255 ma_uint64 i; 43256 for (i = 0; i < count; i += 1) { 43257 ma_int32 x = src_u8[i]; 43258 x = x - 128; 43259 x = x << 24; 43260 dst_s32[i] = x; 43261 } 43262 43263 (void)ditherMode; 43264 } 43265 43266 static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43267 { 43268 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); 43269 } 43270 43271 #if defined(MA_SUPPORT_SSE2) 43272 static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43273 { 43274 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); 43275 } 43276 #endif 43277 #if defined(MA_SUPPORT_NEON) 43278 static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43279 { 43280 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); 43281 } 43282 #endif 43283 43284 MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43285 { 43286 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43287 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); 43288 #else 43289 # if defined(MA_SUPPORT_SSE2) 43290 if (ma_has_sse2()) { 43291 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); 43292 } else 43293 #elif defined(MA_SUPPORT_NEON) 43294 if (ma_has_neon()) { 43295 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); 43296 } else 43297 #endif 43298 { 43299 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); 43300 } 43301 #endif 43302 } 43303 43304 43305 static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43306 { 43307 float* dst_f32 = (float*)dst; 43308 const ma_uint8* src_u8 = (const ma_uint8*)src; 43309 43310 ma_uint64 i; 43311 for (i = 0; i < count; i += 1) { 43312 float x = (float)src_u8[i]; 43313 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ 43314 x = x - 1; /* 0..2 to -1..1 */ 43315 43316 dst_f32[i] = x; 43317 } 43318 43319 (void)ditherMode; 43320 } 43321 43322 static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43323 { 43324 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); 43325 } 43326 43327 #if defined(MA_SUPPORT_SSE2) 43328 static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43329 { 43330 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); 43331 } 43332 #endif 43333 #if defined(MA_SUPPORT_NEON) 43334 static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43335 { 43336 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); 43337 } 43338 #endif 43339 43340 MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43341 { 43342 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43343 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); 43344 #else 43345 # if defined(MA_SUPPORT_SSE2) 43346 if (ma_has_sse2()) { 43347 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); 43348 } else 43349 #elif defined(MA_SUPPORT_NEON) 43350 if (ma_has_neon()) { 43351 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); 43352 } else 43353 #endif 43354 { 43355 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); 43356 } 43357 #endif 43358 } 43359 43360 43361 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43362 static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43363 { 43364 ma_uint8* dst_u8 = (ma_uint8*)dst; 43365 const ma_uint8** src_u8 = (const ma_uint8**)src; 43366 43367 ma_uint64 iFrame; 43368 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43369 ma_uint32 iChannel; 43370 for (iChannel = 0; iChannel < channels; iChannel += 1) { 43371 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; 43372 } 43373 } 43374 } 43375 #else 43376 static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43377 { 43378 ma_uint8* dst_u8 = (ma_uint8*)dst; 43379 const ma_uint8** src_u8 = (const ma_uint8**)src; 43380 43381 if (channels == 1) { 43382 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); 43383 } else if (channels == 2) { 43384 ma_uint64 iFrame; 43385 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43386 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; 43387 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; 43388 } 43389 } else { 43390 ma_uint64 iFrame; 43391 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43392 ma_uint32 iChannel; 43393 for (iChannel = 0; iChannel < channels; iChannel += 1) { 43394 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; 43395 } 43396 } 43397 } 43398 } 43399 #endif 43400 43401 MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43402 { 43403 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43404 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); 43405 #else 43406 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); 43407 #endif 43408 } 43409 43410 43411 static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43412 { 43413 ma_uint8** dst_u8 = (ma_uint8**)dst; 43414 const ma_uint8* src_u8 = (const ma_uint8*)src; 43415 43416 ma_uint64 iFrame; 43417 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43418 ma_uint32 iChannel; 43419 for (iChannel = 0; iChannel < channels; iChannel += 1) { 43420 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; 43421 } 43422 } 43423 } 43424 43425 static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43426 { 43427 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); 43428 } 43429 43430 MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43431 { 43432 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43433 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); 43434 #else 43435 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); 43436 #endif 43437 } 43438 43439 43440 /* s16 */ 43441 static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43442 { 43443 ma_uint8* dst_u8 = (ma_uint8*)dst; 43444 const ma_int16* src_s16 = (const ma_int16*)src; 43445 43446 if (ditherMode == ma_dither_mode_none) { 43447 ma_uint64 i; 43448 for (i = 0; i < count; i += 1) { 43449 ma_int16 x = src_s16[i]; 43450 x = (ma_int16)(x >> 8); 43451 x = (ma_int16)(x + 128); 43452 dst_u8[i] = (ma_uint8)x; 43453 } 43454 } else { 43455 ma_uint64 i; 43456 for (i = 0; i < count; i += 1) { 43457 ma_int16 x = src_s16[i]; 43458 43459 /* Dither. Don't overflow. */ 43460 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); 43461 if ((x + dither) <= 0x7FFF) { 43462 x = (ma_int16)(x + dither); 43463 } else { 43464 x = 0x7FFF; 43465 } 43466 43467 x = (ma_int16)(x >> 8); 43468 x = (ma_int16)(x + 128); 43469 dst_u8[i] = (ma_uint8)x; 43470 } 43471 } 43472 } 43473 43474 static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43475 { 43476 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); 43477 } 43478 43479 #if defined(MA_SUPPORT_SSE2) 43480 static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43481 { 43482 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); 43483 } 43484 #endif 43485 #if defined(MA_SUPPORT_NEON) 43486 static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43487 { 43488 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); 43489 } 43490 #endif 43491 43492 MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43493 { 43494 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43495 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); 43496 #else 43497 # if defined(MA_SUPPORT_SSE2) 43498 if (ma_has_sse2()) { 43499 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); 43500 } else 43501 #elif defined(MA_SUPPORT_NEON) 43502 if (ma_has_neon()) { 43503 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); 43504 } else 43505 #endif 43506 { 43507 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); 43508 } 43509 #endif 43510 } 43511 43512 43513 MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43514 { 43515 (void)ditherMode; 43516 ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); 43517 } 43518 43519 43520 static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43521 { 43522 ma_uint8* dst_s24 = (ma_uint8*)dst; 43523 const ma_int16* src_s16 = (const ma_int16*)src; 43524 43525 ma_uint64 i; 43526 for (i = 0; i < count; i += 1) { 43527 dst_s24[i*3+0] = 0; 43528 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); 43529 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); 43530 } 43531 43532 (void)ditherMode; 43533 } 43534 43535 static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43536 { 43537 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); 43538 } 43539 43540 #if defined(MA_SUPPORT_SSE2) 43541 static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43542 { 43543 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); 43544 } 43545 #endif 43546 #if defined(MA_SUPPORT_NEON) 43547 static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43548 { 43549 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); 43550 } 43551 #endif 43552 43553 MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43554 { 43555 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43556 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); 43557 #else 43558 # if defined(MA_SUPPORT_SSE2) 43559 if (ma_has_sse2()) { 43560 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); 43561 } else 43562 #elif defined(MA_SUPPORT_NEON) 43563 if (ma_has_neon()) { 43564 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); 43565 } else 43566 #endif 43567 { 43568 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); 43569 } 43570 #endif 43571 } 43572 43573 43574 static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43575 { 43576 ma_int32* dst_s32 = (ma_int32*)dst; 43577 const ma_int16* src_s16 = (const ma_int16*)src; 43578 43579 ma_uint64 i; 43580 for (i = 0; i < count; i += 1) { 43581 dst_s32[i] = src_s16[i] << 16; 43582 } 43583 43584 (void)ditherMode; 43585 } 43586 43587 static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43588 { 43589 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); 43590 } 43591 43592 #if defined(MA_SUPPORT_SSE2) 43593 static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43594 { 43595 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); 43596 } 43597 #endif 43598 #if defined(MA_SUPPORT_NEON) 43599 static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43600 { 43601 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); 43602 } 43603 #endif 43604 43605 MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43606 { 43607 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43608 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); 43609 #else 43610 # if defined(MA_SUPPORT_SSE2) 43611 if (ma_has_sse2()) { 43612 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); 43613 } else 43614 #elif defined(MA_SUPPORT_NEON) 43615 if (ma_has_neon()) { 43616 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); 43617 } else 43618 #endif 43619 { 43620 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); 43621 } 43622 #endif 43623 } 43624 43625 43626 static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43627 { 43628 float* dst_f32 = (float*)dst; 43629 const ma_int16* src_s16 = (const ma_int16*)src; 43630 43631 ma_uint64 i; 43632 for (i = 0; i < count; i += 1) { 43633 float x = (float)src_s16[i]; 43634 43635 #if 0 43636 /* The accurate way. */ 43637 x = x + 32768.0f; /* -32768..32767 to 0..65535 */ 43638 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ 43639 x = x - 1; /* 0..2 to -1..1 */ 43640 #else 43641 /* The fast way. */ 43642 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ 43643 #endif 43644 43645 dst_f32[i] = x; 43646 } 43647 43648 (void)ditherMode; 43649 } 43650 43651 static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43652 { 43653 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); 43654 } 43655 43656 #if defined(MA_SUPPORT_SSE2) 43657 static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43658 { 43659 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); 43660 } 43661 #endif 43662 #if defined(MA_SUPPORT_NEON) 43663 static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43664 { 43665 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); 43666 } 43667 #endif 43668 43669 MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43670 { 43671 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43672 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); 43673 #else 43674 # if defined(MA_SUPPORT_SSE2) 43675 if (ma_has_sse2()) { 43676 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); 43677 } else 43678 #elif defined(MA_SUPPORT_NEON) 43679 if (ma_has_neon()) { 43680 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); 43681 } else 43682 #endif 43683 { 43684 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); 43685 } 43686 #endif 43687 } 43688 43689 43690 static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43691 { 43692 ma_int16* dst_s16 = (ma_int16*)dst; 43693 const ma_int16** src_s16 = (const ma_int16**)src; 43694 43695 ma_uint64 iFrame; 43696 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43697 ma_uint32 iChannel; 43698 for (iChannel = 0; iChannel < channels; iChannel += 1) { 43699 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; 43700 } 43701 } 43702 } 43703 43704 static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43705 { 43706 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); 43707 } 43708 43709 MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 43710 { 43711 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43712 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); 43713 #else 43714 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); 43715 #endif 43716 } 43717 43718 43719 static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43720 { 43721 ma_int16** dst_s16 = (ma_int16**)dst; 43722 const ma_int16* src_s16 = (const ma_int16*)src; 43723 43724 ma_uint64 iFrame; 43725 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 43726 ma_uint32 iChannel; 43727 for (iChannel = 0; iChannel < channels; iChannel += 1) { 43728 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; 43729 } 43730 } 43731 } 43732 43733 static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43734 { 43735 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); 43736 } 43737 43738 MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 43739 { 43740 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43741 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); 43742 #else 43743 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); 43744 #endif 43745 } 43746 43747 43748 /* s24 */ 43749 static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43750 { 43751 ma_uint8* dst_u8 = (ma_uint8*)dst; 43752 const ma_uint8* src_s24 = (const ma_uint8*)src; 43753 43754 if (ditherMode == ma_dither_mode_none) { 43755 ma_uint64 i; 43756 for (i = 0; i < count; i += 1) { 43757 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); 43758 } 43759 } else { 43760 ma_uint64 i; 43761 for (i = 0; i < count; i += 1) { 43762 ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); 43763 43764 /* Dither. Don't overflow. */ 43765 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); 43766 if ((ma_int64)x + dither <= 0x7FFFFFFF) { 43767 x = x + dither; 43768 } else { 43769 x = 0x7FFFFFFF; 43770 } 43771 43772 x = x >> 24; 43773 x = x + 128; 43774 dst_u8[i] = (ma_uint8)x; 43775 } 43776 } 43777 } 43778 43779 static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43780 { 43781 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); 43782 } 43783 43784 #if defined(MA_SUPPORT_SSE2) 43785 static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43786 { 43787 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); 43788 } 43789 #endif 43790 #if defined(MA_SUPPORT_NEON) 43791 static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43792 { 43793 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); 43794 } 43795 #endif 43796 43797 MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43798 { 43799 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43800 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); 43801 #else 43802 # if defined(MA_SUPPORT_SSE2) 43803 if (ma_has_sse2()) { 43804 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); 43805 } else 43806 #elif defined(MA_SUPPORT_NEON) 43807 if (ma_has_neon()) { 43808 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); 43809 } else 43810 #endif 43811 { 43812 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); 43813 } 43814 #endif 43815 } 43816 43817 43818 static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43819 { 43820 ma_int16* dst_s16 = (ma_int16*)dst; 43821 const ma_uint8* src_s24 = (const ma_uint8*)src; 43822 43823 if (ditherMode == ma_dither_mode_none) { 43824 ma_uint64 i; 43825 for (i = 0; i < count; i += 1) { 43826 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); 43827 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); 43828 dst_s16[i] = (ma_int16)(dst_lo | dst_hi); 43829 } 43830 } else { 43831 ma_uint64 i; 43832 for (i = 0; i < count; i += 1) { 43833 ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); 43834 43835 /* Dither. Don't overflow. */ 43836 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); 43837 if ((ma_int64)x + dither <= 0x7FFFFFFF) { 43838 x = x + dither; 43839 } else { 43840 x = 0x7FFFFFFF; 43841 } 43842 43843 x = x >> 16; 43844 dst_s16[i] = (ma_int16)x; 43845 } 43846 } 43847 } 43848 43849 static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43850 { 43851 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); 43852 } 43853 43854 #if defined(MA_SUPPORT_SSE2) 43855 static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43856 { 43857 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); 43858 } 43859 #endif 43860 #if defined(MA_SUPPORT_NEON) 43861 static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43862 { 43863 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); 43864 } 43865 #endif 43866 43867 MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43868 { 43869 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43870 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); 43871 #else 43872 # if defined(MA_SUPPORT_SSE2) 43873 if (ma_has_sse2()) { 43874 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); 43875 } else 43876 #elif defined(MA_SUPPORT_NEON) 43877 if (ma_has_neon()) { 43878 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); 43879 } else 43880 #endif 43881 { 43882 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); 43883 } 43884 #endif 43885 } 43886 43887 43888 MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43889 { 43890 (void)ditherMode; 43891 43892 ma_copy_memory_64(dst, src, count * 3); 43893 } 43894 43895 43896 static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43897 { 43898 ma_int32* dst_s32 = (ma_int32*)dst; 43899 const ma_uint8* src_s24 = (const ma_uint8*)src; 43900 43901 ma_uint64 i; 43902 for (i = 0; i < count; i += 1) { 43903 dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); 43904 } 43905 43906 (void)ditherMode; 43907 } 43908 43909 static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43910 { 43911 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); 43912 } 43913 43914 #if defined(MA_SUPPORT_SSE2) 43915 static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43916 { 43917 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); 43918 } 43919 #endif 43920 #if defined(MA_SUPPORT_NEON) 43921 static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43922 { 43923 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); 43924 } 43925 #endif 43926 43927 MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43928 { 43929 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43930 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); 43931 #else 43932 # if defined(MA_SUPPORT_SSE2) 43933 if (ma_has_sse2()) { 43934 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); 43935 } else 43936 #elif defined(MA_SUPPORT_NEON) 43937 if (ma_has_neon()) { 43938 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); 43939 } else 43940 #endif 43941 { 43942 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); 43943 } 43944 #endif 43945 } 43946 43947 43948 static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43949 { 43950 float* dst_f32 = (float*)dst; 43951 const ma_uint8* src_s24 = (const ma_uint8*)src; 43952 43953 ma_uint64 i; 43954 for (i = 0; i < count; i += 1) { 43955 float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); 43956 43957 #if 0 43958 /* The accurate way. */ 43959 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ 43960 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ 43961 x = x - 1; /* 0..2 to -1..1 */ 43962 #else 43963 /* The fast way. */ 43964 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ 43965 #endif 43966 43967 dst_f32[i] = x; 43968 } 43969 43970 (void)ditherMode; 43971 } 43972 43973 static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43974 { 43975 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); 43976 } 43977 43978 #if defined(MA_SUPPORT_SSE2) 43979 static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43980 { 43981 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); 43982 } 43983 #endif 43984 #if defined(MA_SUPPORT_NEON) 43985 static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43986 { 43987 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); 43988 } 43989 #endif 43990 43991 MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 43992 { 43993 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 43994 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); 43995 #else 43996 # if defined(MA_SUPPORT_SSE2) 43997 if (ma_has_sse2()) { 43998 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); 43999 } else 44000 #elif defined(MA_SUPPORT_NEON) 44001 if (ma_has_neon()) { 44002 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); 44003 } else 44004 #endif 44005 { 44006 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); 44007 } 44008 #endif 44009 } 44010 44011 44012 static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44013 { 44014 ma_uint8* dst8 = (ma_uint8*)dst; 44015 const ma_uint8** src8 = (const ma_uint8**)src; 44016 44017 ma_uint64 iFrame; 44018 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44019 ma_uint32 iChannel; 44020 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44021 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; 44022 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; 44023 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; 44024 } 44025 } 44026 } 44027 44028 static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44029 { 44030 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); 44031 } 44032 44033 MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44034 { 44035 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44036 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); 44037 #else 44038 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); 44039 #endif 44040 } 44041 44042 44043 static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44044 { 44045 ma_uint8** dst8 = (ma_uint8**)dst; 44046 const ma_uint8* src8 = (const ma_uint8*)src; 44047 44048 ma_uint32 iFrame; 44049 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44050 ma_uint32 iChannel; 44051 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44052 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; 44053 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; 44054 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; 44055 } 44056 } 44057 } 44058 44059 static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44060 { 44061 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); 44062 } 44063 44064 MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44065 { 44066 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44067 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); 44068 #else 44069 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); 44070 #endif 44071 } 44072 44073 44074 44075 /* s32 */ 44076 static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44077 { 44078 ma_uint8* dst_u8 = (ma_uint8*)dst; 44079 const ma_int32* src_s32 = (const ma_int32*)src; 44080 44081 if (ditherMode == ma_dither_mode_none) { 44082 ma_uint64 i; 44083 for (i = 0; i < count; i += 1) { 44084 ma_int32 x = src_s32[i]; 44085 x = x >> 24; 44086 x = x + 128; 44087 dst_u8[i] = (ma_uint8)x; 44088 } 44089 } else { 44090 ma_uint64 i; 44091 for (i = 0; i < count; i += 1) { 44092 ma_int32 x = src_s32[i]; 44093 44094 /* Dither. Don't overflow. */ 44095 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); 44096 if ((ma_int64)x + dither <= 0x7FFFFFFF) { 44097 x = x + dither; 44098 } else { 44099 x = 0x7FFFFFFF; 44100 } 44101 44102 x = x >> 24; 44103 x = x + 128; 44104 dst_u8[i] = (ma_uint8)x; 44105 } 44106 } 44107 } 44108 44109 static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44110 { 44111 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); 44112 } 44113 44114 #if defined(MA_SUPPORT_SSE2) 44115 static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44116 { 44117 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); 44118 } 44119 #endif 44120 #if defined(MA_SUPPORT_NEON) 44121 static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44122 { 44123 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); 44124 } 44125 #endif 44126 44127 MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44128 { 44129 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44130 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); 44131 #else 44132 # if defined(MA_SUPPORT_SSE2) 44133 if (ma_has_sse2()) { 44134 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); 44135 } else 44136 #elif defined(MA_SUPPORT_NEON) 44137 if (ma_has_neon()) { 44138 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); 44139 } else 44140 #endif 44141 { 44142 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); 44143 } 44144 #endif 44145 } 44146 44147 44148 static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44149 { 44150 ma_int16* dst_s16 = (ma_int16*)dst; 44151 const ma_int32* src_s32 = (const ma_int32*)src; 44152 44153 if (ditherMode == ma_dither_mode_none) { 44154 ma_uint64 i; 44155 for (i = 0; i < count; i += 1) { 44156 ma_int32 x = src_s32[i]; 44157 x = x >> 16; 44158 dst_s16[i] = (ma_int16)x; 44159 } 44160 } else { 44161 ma_uint64 i; 44162 for (i = 0; i < count; i += 1) { 44163 ma_int32 x = src_s32[i]; 44164 44165 /* Dither. Don't overflow. */ 44166 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); 44167 if ((ma_int64)x + dither <= 0x7FFFFFFF) { 44168 x = x + dither; 44169 } else { 44170 x = 0x7FFFFFFF; 44171 } 44172 44173 x = x >> 16; 44174 dst_s16[i] = (ma_int16)x; 44175 } 44176 } 44177 } 44178 44179 static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44180 { 44181 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); 44182 } 44183 44184 #if defined(MA_SUPPORT_SSE2) 44185 static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44186 { 44187 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); 44188 } 44189 #endif 44190 #if defined(MA_SUPPORT_NEON) 44191 static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44192 { 44193 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); 44194 } 44195 #endif 44196 44197 MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44198 { 44199 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44200 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); 44201 #else 44202 # if defined(MA_SUPPORT_SSE2) 44203 if (ma_has_sse2()) { 44204 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); 44205 } else 44206 #elif defined(MA_SUPPORT_NEON) 44207 if (ma_has_neon()) { 44208 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); 44209 } else 44210 #endif 44211 { 44212 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); 44213 } 44214 #endif 44215 } 44216 44217 44218 static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44219 { 44220 ma_uint8* dst_s24 = (ma_uint8*)dst; 44221 const ma_int32* src_s32 = (const ma_int32*)src; 44222 44223 ma_uint64 i; 44224 for (i = 0; i < count; i += 1) { 44225 ma_uint32 x = (ma_uint32)src_s32[i]; 44226 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); 44227 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); 44228 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); 44229 } 44230 44231 (void)ditherMode; /* No dithering for s32 -> s24. */ 44232 } 44233 44234 static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44235 { 44236 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); 44237 } 44238 44239 #if defined(MA_SUPPORT_SSE2) 44240 static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44241 { 44242 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); 44243 } 44244 #endif 44245 #if defined(MA_SUPPORT_NEON) 44246 static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44247 { 44248 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); 44249 } 44250 #endif 44251 44252 MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44253 { 44254 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44255 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); 44256 #else 44257 # if defined(MA_SUPPORT_SSE2) 44258 if (ma_has_sse2()) { 44259 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); 44260 } else 44261 #elif defined(MA_SUPPORT_NEON) 44262 if (ma_has_neon()) { 44263 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); 44264 } else 44265 #endif 44266 { 44267 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); 44268 } 44269 #endif 44270 } 44271 44272 44273 MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44274 { 44275 (void)ditherMode; 44276 44277 ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); 44278 } 44279 44280 44281 static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44282 { 44283 float* dst_f32 = (float*)dst; 44284 const ma_int32* src_s32 = (const ma_int32*)src; 44285 44286 ma_uint64 i; 44287 for (i = 0; i < count; i += 1) { 44288 double x = src_s32[i]; 44289 44290 #if 0 44291 x = x + 2147483648.0; 44292 x = x * 0.0000000004656612873077392578125; 44293 x = x - 1; 44294 #else 44295 x = x / 2147483648.0; 44296 #endif 44297 44298 dst_f32[i] = (float)x; 44299 } 44300 44301 (void)ditherMode; /* No dithering for s32 -> f32. */ 44302 } 44303 44304 static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44305 { 44306 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); 44307 } 44308 44309 #if defined(MA_SUPPORT_SSE2) 44310 static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44311 { 44312 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); 44313 } 44314 #endif 44315 #if defined(MA_SUPPORT_NEON) 44316 static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44317 { 44318 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); 44319 } 44320 #endif 44321 44322 MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44323 { 44324 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44325 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); 44326 #else 44327 # if defined(MA_SUPPORT_SSE2) 44328 if (ma_has_sse2()) { 44329 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); 44330 } else 44331 #elif defined(MA_SUPPORT_NEON) 44332 if (ma_has_neon()) { 44333 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); 44334 } else 44335 #endif 44336 { 44337 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); 44338 } 44339 #endif 44340 } 44341 44342 44343 static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44344 { 44345 ma_int32* dst_s32 = (ma_int32*)dst; 44346 const ma_int32** src_s32 = (const ma_int32**)src; 44347 44348 ma_uint64 iFrame; 44349 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44350 ma_uint32 iChannel; 44351 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44352 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; 44353 } 44354 } 44355 } 44356 44357 static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44358 { 44359 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); 44360 } 44361 44362 MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44363 { 44364 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44365 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); 44366 #else 44367 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); 44368 #endif 44369 } 44370 44371 44372 static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44373 { 44374 ma_int32** dst_s32 = (ma_int32**)dst; 44375 const ma_int32* src_s32 = (const ma_int32*)src; 44376 44377 ma_uint64 iFrame; 44378 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44379 ma_uint32 iChannel; 44380 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44381 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; 44382 } 44383 } 44384 } 44385 44386 static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44387 { 44388 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); 44389 } 44390 44391 MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44392 { 44393 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44394 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); 44395 #else 44396 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); 44397 #endif 44398 } 44399 44400 44401 /* f32 */ 44402 static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44403 { 44404 ma_uint64 i; 44405 44406 ma_uint8* dst_u8 = (ma_uint8*)dst; 44407 const float* src_f32 = (const float*)src; 44408 44409 float ditherMin = 0; 44410 float ditherMax = 0; 44411 if (ditherMode != ma_dither_mode_none) { 44412 ditherMin = 1.0f / -128; 44413 ditherMax = 1.0f / 127; 44414 } 44415 44416 for (i = 0; i < count; i += 1) { 44417 float x = src_f32[i]; 44418 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); 44419 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44420 x = x + 1; /* -1..1 to 0..2 */ 44421 x = x * 127.5f; /* 0..2 to 0..255 */ 44422 44423 dst_u8[i] = (ma_uint8)x; 44424 } 44425 } 44426 44427 static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44428 { 44429 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); 44430 } 44431 44432 #if defined(MA_SUPPORT_SSE2) 44433 static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44434 { 44435 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); 44436 } 44437 #endif 44438 #if defined(MA_SUPPORT_NEON) 44439 static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44440 { 44441 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); 44442 } 44443 #endif 44444 44445 MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44446 { 44447 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44448 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); 44449 #else 44450 # if defined(MA_SUPPORT_SSE2) 44451 if (ma_has_sse2()) { 44452 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); 44453 } else 44454 #elif defined(MA_SUPPORT_NEON) 44455 if (ma_has_neon()) { 44456 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); 44457 } else 44458 #endif 44459 { 44460 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); 44461 } 44462 #endif 44463 } 44464 44465 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44466 static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44467 { 44468 ma_uint64 i; 44469 44470 ma_int16* dst_s16 = (ma_int16*)dst; 44471 const float* src_f32 = (const float*)src; 44472 44473 float ditherMin = 0; 44474 float ditherMax = 0; 44475 if (ditherMode != ma_dither_mode_none) { 44476 ditherMin = 1.0f / -32768; 44477 ditherMax = 1.0f / 32767; 44478 } 44479 44480 for (i = 0; i < count; i += 1) { 44481 float x = src_f32[i]; 44482 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); 44483 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44484 44485 #if 0 44486 /* The accurate way. */ 44487 x = x + 1; /* -1..1 to 0..2 */ 44488 x = x * 32767.5f; /* 0..2 to 0..65535 */ 44489 x = x - 32768.0f; /* 0...65535 to -32768..32767 */ 44490 #else 44491 /* The fast way. */ 44492 x = x * 32767.0f; /* -1..1 to -32767..32767 */ 44493 #endif 44494 44495 dst_s16[i] = (ma_int16)x; 44496 } 44497 } 44498 #else 44499 static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44500 { 44501 ma_uint64 i; 44502 ma_uint64 i4; 44503 ma_uint64 count4; 44504 44505 ma_int16* dst_s16 = (ma_int16*)dst; 44506 const float* src_f32 = (const float*)src; 44507 44508 float ditherMin = 0; 44509 float ditherMax = 0; 44510 if (ditherMode != ma_dither_mode_none) { 44511 ditherMin = 1.0f / -32768; 44512 ditherMax = 1.0f / 32767; 44513 } 44514 44515 /* Unrolled. */ 44516 i = 0; 44517 count4 = count >> 2; 44518 for (i4 = 0; i4 < count4; i4 += 1) { 44519 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); 44520 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); 44521 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); 44522 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); 44523 44524 float x0 = src_f32[i+0]; 44525 float x1 = src_f32[i+1]; 44526 float x2 = src_f32[i+2]; 44527 float x3 = src_f32[i+3]; 44528 44529 x0 = x0 + d0; 44530 x1 = x1 + d1; 44531 x2 = x2 + d2; 44532 x3 = x3 + d3; 44533 44534 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); 44535 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); 44536 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); 44537 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); 44538 44539 x0 = x0 * 32767.0f; 44540 x1 = x1 * 32767.0f; 44541 x2 = x2 * 32767.0f; 44542 x3 = x3 * 32767.0f; 44543 44544 dst_s16[i+0] = (ma_int16)x0; 44545 dst_s16[i+1] = (ma_int16)x1; 44546 dst_s16[i+2] = (ma_int16)x2; 44547 dst_s16[i+3] = (ma_int16)x3; 44548 44549 i += 4; 44550 } 44551 44552 /* Leftover. */ 44553 for (; i < count; i += 1) { 44554 float x = src_f32[i]; 44555 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); 44556 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44557 x = x * 32767.0f; /* -1..1 to -32767..32767 */ 44558 44559 dst_s16[i] = (ma_int16)x; 44560 } 44561 } 44562 44563 #if defined(MA_SUPPORT_SSE2) 44564 static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44565 { 44566 ma_uint64 i; 44567 ma_uint64 i8; 44568 ma_uint64 count8; 44569 ma_int16* dst_s16; 44570 const float* src_f32; 44571 float ditherMin; 44572 float ditherMax; 44573 44574 /* Both the input and output buffers need to be aligned to 16 bytes. */ 44575 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { 44576 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); 44577 return; 44578 } 44579 44580 dst_s16 = (ma_int16*)dst; 44581 src_f32 = (const float*)src; 44582 44583 ditherMin = 0; 44584 ditherMax = 0; 44585 if (ditherMode != ma_dither_mode_none) { 44586 ditherMin = 1.0f / -32768; 44587 ditherMax = 1.0f / 32767; 44588 } 44589 44590 i = 0; 44591 44592 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ 44593 count8 = count >> 3; 44594 for (i8 = 0; i8 < count8; i8 += 1) { 44595 __m128 d0; 44596 __m128 d1; 44597 __m128 x0; 44598 __m128 x1; 44599 44600 if (ditherMode == ma_dither_mode_none) { 44601 d0 = _mm_set1_ps(0); 44602 d1 = _mm_set1_ps(0); 44603 } else if (ditherMode == ma_dither_mode_rectangle) { 44604 d0 = _mm_set_ps( 44605 ma_dither_f32_rectangle(ditherMin, ditherMax), 44606 ma_dither_f32_rectangle(ditherMin, ditherMax), 44607 ma_dither_f32_rectangle(ditherMin, ditherMax), 44608 ma_dither_f32_rectangle(ditherMin, ditherMax) 44609 ); 44610 d1 = _mm_set_ps( 44611 ma_dither_f32_rectangle(ditherMin, ditherMax), 44612 ma_dither_f32_rectangle(ditherMin, ditherMax), 44613 ma_dither_f32_rectangle(ditherMin, ditherMax), 44614 ma_dither_f32_rectangle(ditherMin, ditherMax) 44615 ); 44616 } else { 44617 d0 = _mm_set_ps( 44618 ma_dither_f32_triangle(ditherMin, ditherMax), 44619 ma_dither_f32_triangle(ditherMin, ditherMax), 44620 ma_dither_f32_triangle(ditherMin, ditherMax), 44621 ma_dither_f32_triangle(ditherMin, ditherMax) 44622 ); 44623 d1 = _mm_set_ps( 44624 ma_dither_f32_triangle(ditherMin, ditherMax), 44625 ma_dither_f32_triangle(ditherMin, ditherMax), 44626 ma_dither_f32_triangle(ditherMin, ditherMax), 44627 ma_dither_f32_triangle(ditherMin, ditherMax) 44628 ); 44629 } 44630 44631 x0 = *((__m128*)(src_f32 + i) + 0); 44632 x1 = *((__m128*)(src_f32 + i) + 1); 44633 44634 x0 = _mm_add_ps(x0, d0); 44635 x1 = _mm_add_ps(x1, d1); 44636 44637 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); 44638 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); 44639 44640 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); 44641 44642 i += 8; 44643 } 44644 44645 44646 /* Leftover. */ 44647 for (; i < count; i += 1) { 44648 float x = src_f32[i]; 44649 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); 44650 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44651 x = x * 32767.0f; /* -1..1 to -32767..32767 */ 44652 44653 dst_s16[i] = (ma_int16)x; 44654 } 44655 } 44656 #endif /* SSE2 */ 44657 44658 #if defined(MA_SUPPORT_NEON) 44659 static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44660 { 44661 ma_uint64 i; 44662 ma_uint64 i8; 44663 ma_uint64 count8; 44664 ma_int16* dst_s16; 44665 const float* src_f32; 44666 float ditherMin; 44667 float ditherMax; 44668 44669 if (!ma_has_neon()) { 44670 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); 44671 return; 44672 } 44673 44674 /* Both the input and output buffers need to be aligned to 16 bytes. */ 44675 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { 44676 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); 44677 return; 44678 } 44679 44680 dst_s16 = (ma_int16*)dst; 44681 src_f32 = (const float*)src; 44682 44683 ditherMin = 0; 44684 ditherMax = 0; 44685 if (ditherMode != ma_dither_mode_none) { 44686 ditherMin = 1.0f / -32768; 44687 ditherMax = 1.0f / 32767; 44688 } 44689 44690 i = 0; 44691 44692 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ 44693 count8 = count >> 3; 44694 for (i8 = 0; i8 < count8; i8 += 1) { 44695 float32x4_t d0; 44696 float32x4_t d1; 44697 float32x4_t x0; 44698 float32x4_t x1; 44699 int32x4_t i0; 44700 int32x4_t i1; 44701 44702 if (ditherMode == ma_dither_mode_none) { 44703 d0 = vmovq_n_f32(0); 44704 d1 = vmovq_n_f32(0); 44705 } else if (ditherMode == ma_dither_mode_rectangle) { 44706 float d0v[4]; 44707 float d1v[4]; 44708 44709 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44710 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44711 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44712 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44713 d0 = vld1q_f32(d0v); 44714 44715 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44716 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44717 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44718 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); 44719 d1 = vld1q_f32(d1v); 44720 } else { 44721 float d0v[4]; 44722 float d1v[4]; 44723 44724 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); 44725 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); 44726 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); 44727 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); 44728 d0 = vld1q_f32(d0v); 44729 44730 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); 44731 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); 44732 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); 44733 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); 44734 d1 = vld1q_f32(d1v); 44735 } 44736 44737 x0 = *((float32x4_t*)(src_f32 + i) + 0); 44738 x1 = *((float32x4_t*)(src_f32 + i) + 1); 44739 44740 x0 = vaddq_f32(x0, d0); 44741 x1 = vaddq_f32(x1, d1); 44742 44743 x0 = vmulq_n_f32(x0, 32767.0f); 44744 x1 = vmulq_n_f32(x1, 32767.0f); 44745 44746 i0 = vcvtq_s32_f32(x0); 44747 i1 = vcvtq_s32_f32(x1); 44748 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); 44749 44750 i += 8; 44751 } 44752 44753 44754 /* Leftover. */ 44755 for (; i < count; i += 1) { 44756 float x = src_f32[i]; 44757 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); 44758 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44759 x = x * 32767.0f; /* -1..1 to -32767..32767 */ 44760 44761 dst_s16[i] = (ma_int16)x; 44762 } 44763 } 44764 #endif /* Neon */ 44765 #endif /* MA_USE_REFERENCE_CONVERSION_APIS */ 44766 44767 MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44768 { 44769 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44770 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); 44771 #else 44772 # if defined(MA_SUPPORT_SSE2) 44773 if (ma_has_sse2()) { 44774 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); 44775 } else 44776 #elif defined(MA_SUPPORT_NEON) 44777 if (ma_has_neon()) { 44778 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); 44779 } else 44780 #endif 44781 { 44782 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); 44783 } 44784 #endif 44785 } 44786 44787 44788 static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44789 { 44790 ma_uint8* dst_s24 = (ma_uint8*)dst; 44791 const float* src_f32 = (const float*)src; 44792 44793 ma_uint64 i; 44794 for (i = 0; i < count; i += 1) { 44795 ma_int32 r; 44796 float x = src_f32[i]; 44797 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44798 44799 #if 0 44800 /* The accurate way. */ 44801 x = x + 1; /* -1..1 to 0..2 */ 44802 x = x * 8388607.5f; /* 0..2 to 0..16777215 */ 44803 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ 44804 #else 44805 /* The fast way. */ 44806 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ 44807 #endif 44808 44809 r = (ma_int32)x; 44810 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); 44811 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); 44812 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); 44813 } 44814 44815 (void)ditherMode; /* No dithering for f32 -> s24. */ 44816 } 44817 44818 static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44819 { 44820 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); 44821 } 44822 44823 #if defined(MA_SUPPORT_SSE2) 44824 static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44825 { 44826 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); 44827 } 44828 #endif 44829 #if defined(MA_SUPPORT_NEON) 44830 static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44831 { 44832 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); 44833 } 44834 #endif 44835 44836 MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44837 { 44838 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44839 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); 44840 #else 44841 # if defined(MA_SUPPORT_SSE2) 44842 if (ma_has_sse2()) { 44843 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); 44844 } else 44845 #elif defined(MA_SUPPORT_NEON) 44846 if (ma_has_neon()) { 44847 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); 44848 } else 44849 #endif 44850 { 44851 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); 44852 } 44853 #endif 44854 } 44855 44856 44857 static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44858 { 44859 ma_int32* dst_s32 = (ma_int32*)dst; 44860 const float* src_f32 = (const float*)src; 44861 44862 ma_uint32 i; 44863 for (i = 0; i < count; i += 1) { 44864 double x = src_f32[i]; 44865 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ 44866 44867 #if 0 44868 /* The accurate way. */ 44869 x = x + 1; /* -1..1 to 0..2 */ 44870 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ 44871 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ 44872 #else 44873 /* The fast way. */ 44874 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ 44875 #endif 44876 44877 dst_s32[i] = (ma_int32)x; 44878 } 44879 44880 (void)ditherMode; /* No dithering for f32 -> s32. */ 44881 } 44882 44883 static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44884 { 44885 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); 44886 } 44887 44888 #if defined(MA_SUPPORT_SSE2) 44889 static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44890 { 44891 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); 44892 } 44893 #endif 44894 #if defined(MA_SUPPORT_NEON) 44895 static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44896 { 44897 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); 44898 } 44899 #endif 44900 44901 MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44902 { 44903 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44904 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); 44905 #else 44906 # if defined(MA_SUPPORT_SSE2) 44907 if (ma_has_sse2()) { 44908 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); 44909 } else 44910 #elif defined(MA_SUPPORT_NEON) 44911 if (ma_has_neon()) { 44912 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); 44913 } else 44914 #endif 44915 { 44916 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); 44917 } 44918 #endif 44919 } 44920 44921 44922 MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) 44923 { 44924 (void)ditherMode; 44925 44926 ma_copy_memory_64(dst, src, count * sizeof(float)); 44927 } 44928 44929 44930 static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44931 { 44932 float* dst_f32 = (float*)dst; 44933 const float** src_f32 = (const float**)src; 44934 44935 ma_uint64 iFrame; 44936 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44937 ma_uint32 iChannel; 44938 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44939 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; 44940 } 44941 } 44942 } 44943 44944 static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44945 { 44946 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); 44947 } 44948 44949 MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) 44950 { 44951 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44952 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); 44953 #else 44954 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); 44955 #endif 44956 } 44957 44958 44959 static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44960 { 44961 float** dst_f32 = (float**)dst; 44962 const float* src_f32 = (const float*)src; 44963 44964 ma_uint64 iFrame; 44965 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 44966 ma_uint32 iChannel; 44967 for (iChannel = 0; iChannel < channels; iChannel += 1) { 44968 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; 44969 } 44970 } 44971 } 44972 44973 static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44974 { 44975 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); 44976 } 44977 44978 MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) 44979 { 44980 #ifdef MA_USE_REFERENCE_CONVERSION_APIS 44981 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); 44982 #else 44983 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); 44984 #endif 44985 } 44986 44987 44988 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) 44989 { 44990 if (formatOut == formatIn) { 44991 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); 44992 return; 44993 } 44994 44995 switch (formatIn) 44996 { 44997 case ma_format_u8: 44998 { 44999 switch (formatOut) 45000 { 45001 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; 45002 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; 45003 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; 45004 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; 45005 default: break; 45006 } 45007 } break; 45008 45009 case ma_format_s16: 45010 { 45011 switch (formatOut) 45012 { 45013 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; 45014 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; 45015 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; 45016 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; 45017 default: break; 45018 } 45019 } break; 45020 45021 case ma_format_s24: 45022 { 45023 switch (formatOut) 45024 { 45025 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; 45026 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; 45027 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; 45028 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; 45029 default: break; 45030 } 45031 } break; 45032 45033 case ma_format_s32: 45034 { 45035 switch (formatOut) 45036 { 45037 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; 45038 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; 45039 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; 45040 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; 45041 default: break; 45042 } 45043 } break; 45044 45045 case ma_format_f32: 45046 { 45047 switch (formatOut) 45048 { 45049 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; 45050 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; 45051 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; 45052 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; 45053 default: break; 45054 } 45055 } break; 45056 45057 default: break; 45058 } 45059 } 45060 45061 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) 45062 { 45063 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); 45064 } 45065 45066 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) 45067 { 45068 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { 45069 return; /* Invalid args. */ 45070 } 45071 45072 /* For efficiency we do this per format. */ 45073 switch (format) { 45074 case ma_format_s16: 45075 { 45076 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; 45077 ma_uint64 iPCMFrame; 45078 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45079 ma_uint32 iChannel; 45080 for (iChannel = 0; iChannel < channels; ++iChannel) { 45081 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; 45082 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; 45083 } 45084 } 45085 } break; 45086 45087 case ma_format_f32: 45088 { 45089 const float* pSrcF32 = (const float*)pInterleavedPCMFrames; 45090 ma_uint64 iPCMFrame; 45091 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45092 ma_uint32 iChannel; 45093 for (iChannel = 0; iChannel < channels; ++iChannel) { 45094 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; 45095 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; 45096 } 45097 } 45098 } break; 45099 45100 default: 45101 { 45102 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); 45103 ma_uint64 iPCMFrame; 45104 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45105 ma_uint32 iChannel; 45106 for (iChannel = 0; iChannel < channels; ++iChannel) { 45107 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); 45108 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); 45109 memcpy(pDst, pSrc, sampleSizeInBytes); 45110 } 45111 } 45112 } break; 45113 } 45114 } 45115 45116 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) 45117 { 45118 switch (format) 45119 { 45120 case ma_format_s16: 45121 { 45122 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; 45123 ma_uint64 iPCMFrame; 45124 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45125 ma_uint32 iChannel; 45126 for (iChannel = 0; iChannel < channels; ++iChannel) { 45127 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; 45128 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; 45129 } 45130 } 45131 } break; 45132 45133 case ma_format_f32: 45134 { 45135 float* pDstF32 = (float*)pInterleavedPCMFrames; 45136 ma_uint64 iPCMFrame; 45137 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45138 ma_uint32 iChannel; 45139 for (iChannel = 0; iChannel < channels; ++iChannel) { 45140 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; 45141 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; 45142 } 45143 } 45144 } break; 45145 45146 default: 45147 { 45148 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); 45149 ma_uint64 iPCMFrame; 45150 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { 45151 ma_uint32 iChannel; 45152 for (iChannel = 0; iChannel < channels; ++iChannel) { 45153 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); 45154 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); 45155 memcpy(pDst, pSrc, sampleSizeInBytes); 45156 } 45157 } 45158 } break; 45159 } 45160 } 45161 45162 45163 /************************************************************************************************************************************************************** 45164 45165 Biquad Filter 45166 45167 **************************************************************************************************************************************************************/ 45168 #ifndef MA_BIQUAD_FIXED_POINT_SHIFT 45169 #define MA_BIQUAD_FIXED_POINT_SHIFT 14 45170 #endif 45171 45172 static ma_int32 ma_biquad_float_to_fp(double x) 45173 { 45174 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); 45175 } 45176 45177 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) 45178 { 45179 ma_biquad_config config; 45180 45181 MA_ZERO_OBJECT(&config); 45182 config.format = format; 45183 config.channels = channels; 45184 config.b0 = b0; 45185 config.b1 = b1; 45186 config.b2 = b2; 45187 config.a0 = a0; 45188 config.a1 = a1; 45189 config.a2 = a2; 45190 45191 return config; 45192 } 45193 45194 45195 typedef struct 45196 { 45197 size_t sizeInBytes; 45198 size_t r1Offset; 45199 size_t r2Offset; 45200 } ma_biquad_heap_layout; 45201 45202 static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) 45203 { 45204 MA_ASSERT(pHeapLayout != NULL); 45205 45206 MA_ZERO_OBJECT(pHeapLayout); 45207 45208 if (pConfig == NULL) { 45209 return MA_INVALID_ARGS; 45210 } 45211 45212 if (pConfig->channels == 0) { 45213 return MA_INVALID_ARGS; 45214 } 45215 45216 pHeapLayout->sizeInBytes = 0; 45217 45218 /* R0 */ 45219 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; 45220 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; 45221 45222 /* R1 */ 45223 pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; 45224 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; 45225 45226 /* Make sure allocation size is aligned. */ 45227 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 45228 45229 return MA_SUCCESS; 45230 } 45231 45232 MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) 45233 { 45234 ma_result result; 45235 ma_biquad_heap_layout heapLayout; 45236 45237 if (pHeapSizeInBytes == NULL) { 45238 return MA_INVALID_ARGS; 45239 } 45240 45241 *pHeapSizeInBytes = 0; 45242 45243 result = ma_biquad_get_heap_layout(pConfig, &heapLayout); 45244 if (result != MA_SUCCESS) { 45245 return result; 45246 } 45247 45248 *pHeapSizeInBytes = heapLayout.sizeInBytes; 45249 45250 return MA_SUCCESS; 45251 } 45252 45253 MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) 45254 { 45255 ma_result result; 45256 ma_biquad_heap_layout heapLayout; 45257 45258 if (pBQ == NULL) { 45259 return MA_INVALID_ARGS; 45260 } 45261 45262 MA_ZERO_OBJECT(pBQ); 45263 45264 result = ma_biquad_get_heap_layout(pConfig, &heapLayout); 45265 if (result != MA_SUCCESS) { 45266 return result; 45267 } 45268 45269 pBQ->_pHeap = pHeap; 45270 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 45271 45272 pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); 45273 pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); 45274 45275 return ma_biquad_reinit(pConfig, pBQ); 45276 } 45277 45278 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) 45279 { 45280 ma_result result; 45281 size_t heapSizeInBytes; 45282 void* pHeap; 45283 45284 result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); 45285 if (result != MA_SUCCESS) { 45286 return result; 45287 } 45288 45289 if (heapSizeInBytes > 0) { 45290 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 45291 if (pHeap == NULL) { 45292 return MA_OUT_OF_MEMORY; 45293 } 45294 } else { 45295 pHeap = NULL; 45296 } 45297 45298 result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); 45299 if (result != MA_SUCCESS) { 45300 ma_free(pHeap, pAllocationCallbacks); 45301 return result; 45302 } 45303 45304 pBQ->_ownsHeap = MA_TRUE; 45305 return MA_SUCCESS; 45306 } 45307 45308 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) 45309 { 45310 if (pBQ == NULL) { 45311 return; 45312 } 45313 45314 if (pBQ->_ownsHeap) { 45315 ma_free(pBQ->_pHeap, pAllocationCallbacks); 45316 } 45317 } 45318 45319 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) 45320 { 45321 if (pBQ == NULL || pConfig == NULL) { 45322 return MA_INVALID_ARGS; 45323 } 45324 45325 if (pConfig->a0 == 0) { 45326 return MA_INVALID_ARGS; /* Division by zero. */ 45327 } 45328 45329 /* Only supporting f32 and s16. */ 45330 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 45331 return MA_INVALID_ARGS; 45332 } 45333 45334 /* The format cannot be changed after initialization. */ 45335 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { 45336 return MA_INVALID_OPERATION; 45337 } 45338 45339 /* The channel count cannot be changed after initialization. */ 45340 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { 45341 return MA_INVALID_OPERATION; 45342 } 45343 45344 45345 pBQ->format = pConfig->format; 45346 pBQ->channels = pConfig->channels; 45347 45348 /* Normalize. */ 45349 if (pConfig->format == ma_format_f32) { 45350 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); 45351 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); 45352 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); 45353 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); 45354 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); 45355 } else { 45356 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); 45357 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); 45358 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); 45359 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); 45360 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); 45361 } 45362 45363 return MA_SUCCESS; 45364 } 45365 45366 MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) 45367 { 45368 if (pBQ == NULL) { 45369 return MA_INVALID_ARGS; 45370 } 45371 45372 if (pBQ->format == ma_format_f32) { 45373 pBQ->pR1->f32 = 0; 45374 pBQ->pR2->f32 = 0; 45375 } else { 45376 pBQ->pR1->s32 = 0; 45377 pBQ->pR2->s32 = 0; 45378 } 45379 45380 return MA_SUCCESS; 45381 } 45382 45383 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) 45384 { 45385 ma_uint32 c; 45386 const ma_uint32 channels = pBQ->channels; 45387 const float b0 = pBQ->b0.f32; 45388 const float b1 = pBQ->b1.f32; 45389 const float b2 = pBQ->b2.f32; 45390 const float a1 = pBQ->a1.f32; 45391 const float a2 = pBQ->a2.f32; 45392 45393 MA_ASSUME(channels > 0); 45394 for (c = 0; c < channels; c += 1) { 45395 float r1 = pBQ->pR1[c].f32; 45396 float r2 = pBQ->pR2[c].f32; 45397 float x = pX[c]; 45398 float y; 45399 45400 y = b0*x + r1; 45401 r1 = b1*x - a1*y + r2; 45402 r2 = b2*x - a2*y; 45403 45404 pY[c] = y; 45405 pBQ->pR1[c].f32 = r1; 45406 pBQ->pR2[c].f32 = r2; 45407 } 45408 } 45409 45410 static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) 45411 { 45412 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); 45413 } 45414 45415 static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) 45416 { 45417 ma_uint32 c; 45418 const ma_uint32 channels = pBQ->channels; 45419 const ma_int32 b0 = pBQ->b0.s32; 45420 const ma_int32 b1 = pBQ->b1.s32; 45421 const ma_int32 b2 = pBQ->b2.s32; 45422 const ma_int32 a1 = pBQ->a1.s32; 45423 const ma_int32 a2 = pBQ->a2.s32; 45424 45425 MA_ASSUME(channels > 0); 45426 for (c = 0; c < channels; c += 1) { 45427 ma_int32 r1 = pBQ->pR1[c].s32; 45428 ma_int32 r2 = pBQ->pR2[c].s32; 45429 ma_int32 x = pX[c]; 45430 ma_int32 y; 45431 45432 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; 45433 r1 = (b1*x - a1*y + r2); 45434 r2 = (b2*x - a2*y); 45435 45436 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); 45437 pBQ->pR1[c].s32 = r1; 45438 pBQ->pR2[c].s32 = r2; 45439 } 45440 } 45441 45442 static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) 45443 { 45444 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); 45445 } 45446 45447 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 45448 { 45449 ma_uint32 n; 45450 45451 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { 45452 return MA_INVALID_ARGS; 45453 } 45454 45455 /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ 45456 45457 if (pBQ->format == ma_format_f32) { 45458 /* */ float* pY = ( float*)pFramesOut; 45459 const float* pX = (const float*)pFramesIn; 45460 45461 for (n = 0; n < frameCount; n += 1) { 45462 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); 45463 pY += pBQ->channels; 45464 pX += pBQ->channels; 45465 } 45466 } else if (pBQ->format == ma_format_s16) { 45467 /* */ ma_int16* pY = ( ma_int16*)pFramesOut; 45468 const ma_int16* pX = (const ma_int16*)pFramesIn; 45469 45470 for (n = 0; n < frameCount; n += 1) { 45471 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); 45472 pY += pBQ->channels; 45473 pX += pBQ->channels; 45474 } 45475 } else { 45476 MA_ASSERT(MA_FALSE); 45477 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ 45478 } 45479 45480 return MA_SUCCESS; 45481 } 45482 45483 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) 45484 { 45485 if (pBQ == NULL) { 45486 return 0; 45487 } 45488 45489 return 2; 45490 } 45491 45492 45493 /************************************************************************************************************************************************************** 45494 45495 Low-Pass Filter 45496 45497 **************************************************************************************************************************************************************/ 45498 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) 45499 { 45500 ma_lpf1_config config; 45501 45502 MA_ZERO_OBJECT(&config); 45503 config.format = format; 45504 config.channels = channels; 45505 config.sampleRate = sampleRate; 45506 config.cutoffFrequency = cutoffFrequency; 45507 config.q = 0.5; 45508 45509 return config; 45510 } 45511 45512 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) 45513 { 45514 ma_lpf2_config config; 45515 45516 MA_ZERO_OBJECT(&config); 45517 config.format = format; 45518 config.channels = channels; 45519 config.sampleRate = sampleRate; 45520 config.cutoffFrequency = cutoffFrequency; 45521 config.q = q; 45522 45523 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ 45524 if (config.q == 0) { 45525 config.q = 0.707107; 45526 } 45527 45528 return config; 45529 } 45530 45531 45532 typedef struct 45533 { 45534 size_t sizeInBytes; 45535 size_t r1Offset; 45536 } ma_lpf1_heap_layout; 45537 45538 static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) 45539 { 45540 MA_ASSERT(pHeapLayout != NULL); 45541 45542 MA_ZERO_OBJECT(pHeapLayout); 45543 45544 if (pConfig == NULL) { 45545 return MA_INVALID_ARGS; 45546 } 45547 45548 if (pConfig->channels == 0) { 45549 return MA_INVALID_ARGS; 45550 } 45551 45552 pHeapLayout->sizeInBytes = 0; 45553 45554 /* R1 */ 45555 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; 45556 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; 45557 45558 /* Make sure allocation size is aligned. */ 45559 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 45560 45561 return MA_SUCCESS; 45562 } 45563 45564 MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) 45565 { 45566 ma_result result; 45567 ma_lpf1_heap_layout heapLayout; 45568 45569 if (pHeapSizeInBytes == NULL) { 45570 return MA_INVALID_ARGS; 45571 } 45572 45573 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); 45574 if (result != MA_SUCCESS) { 45575 return result; 45576 } 45577 45578 *pHeapSizeInBytes = heapLayout.sizeInBytes; 45579 45580 return MA_SUCCESS; 45581 } 45582 45583 MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) 45584 { 45585 ma_result result; 45586 ma_lpf1_heap_layout heapLayout; 45587 45588 if (pLPF == NULL) { 45589 return MA_INVALID_ARGS; 45590 } 45591 45592 MA_ZERO_OBJECT(pLPF); 45593 45594 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); 45595 if (result != MA_SUCCESS) { 45596 return result; 45597 } 45598 45599 pLPF->_pHeap = pHeap; 45600 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 45601 45602 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); 45603 45604 return ma_lpf1_reinit(pConfig, pLPF); 45605 } 45606 45607 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) 45608 { 45609 ma_result result; 45610 size_t heapSizeInBytes; 45611 void* pHeap; 45612 45613 result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); 45614 if (result != MA_SUCCESS) { 45615 return result; 45616 } 45617 45618 if (heapSizeInBytes > 0) { 45619 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 45620 if (pHeap == NULL) { 45621 return MA_OUT_OF_MEMORY; 45622 } 45623 } else { 45624 pHeap = NULL; 45625 } 45626 45627 result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); 45628 if (result != MA_SUCCESS) { 45629 ma_free(pHeap, pAllocationCallbacks); 45630 return result; 45631 } 45632 45633 pLPF->_ownsHeap = MA_TRUE; 45634 return MA_SUCCESS; 45635 } 45636 45637 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) 45638 { 45639 if (pLPF == NULL) { 45640 return; 45641 } 45642 45643 if (pLPF->_ownsHeap) { 45644 ma_free(pLPF->_pHeap, pAllocationCallbacks); 45645 } 45646 } 45647 45648 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) 45649 { 45650 double a; 45651 45652 if (pLPF == NULL || pConfig == NULL) { 45653 return MA_INVALID_ARGS; 45654 } 45655 45656 /* Only supporting f32 and s16. */ 45657 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 45658 return MA_INVALID_ARGS; 45659 } 45660 45661 /* The format cannot be changed after initialization. */ 45662 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { 45663 return MA_INVALID_OPERATION; 45664 } 45665 45666 /* The channel count cannot be changed after initialization. */ 45667 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { 45668 return MA_INVALID_OPERATION; 45669 } 45670 45671 pLPF->format = pConfig->format; 45672 pLPF->channels = pConfig->channels; 45673 45674 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); 45675 if (pConfig->format == ma_format_f32) { 45676 pLPF->a.f32 = (float)a; 45677 } else { 45678 pLPF->a.s32 = ma_biquad_float_to_fp(a); 45679 } 45680 45681 return MA_SUCCESS; 45682 } 45683 45684 MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) 45685 { 45686 if (pLPF == NULL) { 45687 return MA_INVALID_ARGS; 45688 } 45689 45690 if (pLPF->format == ma_format_f32) { 45691 pLPF->a.f32 = 0; 45692 } else { 45693 pLPF->a.s32 = 0; 45694 } 45695 45696 return MA_SUCCESS; 45697 } 45698 45699 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) 45700 { 45701 ma_uint32 c; 45702 const ma_uint32 channels = pLPF->channels; 45703 const float a = pLPF->a.f32; 45704 const float b = 1 - a; 45705 45706 MA_ASSUME(channels > 0); 45707 for (c = 0; c < channels; c += 1) { 45708 float r1 = pLPF->pR1[c].f32; 45709 float x = pX[c]; 45710 float y; 45711 45712 y = b*x + a*r1; 45713 45714 pY[c] = y; 45715 pLPF->pR1[c].f32 = y; 45716 } 45717 } 45718 45719 static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) 45720 { 45721 ma_uint32 c; 45722 const ma_uint32 channels = pLPF->channels; 45723 const ma_int32 a = pLPF->a.s32; 45724 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); 45725 45726 MA_ASSUME(channels > 0); 45727 for (c = 0; c < channels; c += 1) { 45728 ma_int32 r1 = pLPF->pR1[c].s32; 45729 ma_int32 x = pX[c]; 45730 ma_int32 y; 45731 45732 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; 45733 45734 pY[c] = (ma_int16)y; 45735 pLPF->pR1[c].s32 = (ma_int32)y; 45736 } 45737 } 45738 45739 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 45740 { 45741 ma_uint32 n; 45742 45743 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { 45744 return MA_INVALID_ARGS; 45745 } 45746 45747 /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ 45748 45749 if (pLPF->format == ma_format_f32) { 45750 /* */ float* pY = ( float*)pFramesOut; 45751 const float* pX = (const float*)pFramesIn; 45752 45753 for (n = 0; n < frameCount; n += 1) { 45754 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); 45755 pY += pLPF->channels; 45756 pX += pLPF->channels; 45757 } 45758 } else if (pLPF->format == ma_format_s16) { 45759 /* */ ma_int16* pY = ( ma_int16*)pFramesOut; 45760 const ma_int16* pX = (const ma_int16*)pFramesIn; 45761 45762 for (n = 0; n < frameCount; n += 1) { 45763 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); 45764 pY += pLPF->channels; 45765 pX += pLPF->channels; 45766 } 45767 } else { 45768 MA_ASSERT(MA_FALSE); 45769 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ 45770 } 45771 45772 return MA_SUCCESS; 45773 } 45774 45775 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) 45776 { 45777 if (pLPF == NULL) { 45778 return 0; 45779 } 45780 45781 return 1; 45782 } 45783 45784 45785 static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) 45786 { 45787 ma_biquad_config bqConfig; 45788 double q; 45789 double w; 45790 double s; 45791 double c; 45792 double a; 45793 45794 MA_ASSERT(pConfig != NULL); 45795 45796 q = pConfig->q; 45797 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; 45798 s = ma_sind(w); 45799 c = ma_cosd(w); 45800 a = s / (2*q); 45801 45802 bqConfig.b0 = (1 - c) / 2; 45803 bqConfig.b1 = 1 - c; 45804 bqConfig.b2 = (1 - c) / 2; 45805 bqConfig.a0 = 1 + a; 45806 bqConfig.a1 = -2 * c; 45807 bqConfig.a2 = 1 - a; 45808 45809 bqConfig.format = pConfig->format; 45810 bqConfig.channels = pConfig->channels; 45811 45812 return bqConfig; 45813 } 45814 45815 MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) 45816 { 45817 ma_biquad_config bqConfig; 45818 bqConfig = ma_lpf2__get_biquad_config(pConfig); 45819 45820 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 45821 } 45822 45823 MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) 45824 { 45825 ma_result result; 45826 ma_biquad_config bqConfig; 45827 45828 if (pLPF == NULL) { 45829 return MA_INVALID_ARGS; 45830 } 45831 45832 MA_ZERO_OBJECT(pLPF); 45833 45834 if (pConfig == NULL) { 45835 return MA_INVALID_ARGS; 45836 } 45837 45838 bqConfig = ma_lpf2__get_biquad_config(pConfig); 45839 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); 45840 if (result != MA_SUCCESS) { 45841 return result; 45842 } 45843 45844 return MA_SUCCESS; 45845 } 45846 45847 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) 45848 { 45849 ma_result result; 45850 size_t heapSizeInBytes; 45851 void* pHeap; 45852 45853 result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); 45854 if (result != MA_SUCCESS) { 45855 return result; 45856 } 45857 45858 if (heapSizeInBytes > 0) { 45859 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 45860 if (pHeap == NULL) { 45861 return MA_OUT_OF_MEMORY; 45862 } 45863 } else { 45864 pHeap = NULL; 45865 } 45866 45867 result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); 45868 if (result != MA_SUCCESS) { 45869 ma_free(pHeap, pAllocationCallbacks); 45870 return result; 45871 } 45872 45873 pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 45874 return MA_SUCCESS; 45875 } 45876 45877 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) 45878 { 45879 if (pLPF == NULL) { 45880 return; 45881 } 45882 45883 ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 45884 } 45885 45886 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) 45887 { 45888 ma_result result; 45889 ma_biquad_config bqConfig; 45890 45891 if (pLPF == NULL || pConfig == NULL) { 45892 return MA_INVALID_ARGS; 45893 } 45894 45895 bqConfig = ma_lpf2__get_biquad_config(pConfig); 45896 result = ma_biquad_reinit(&bqConfig, &pLPF->bq); 45897 if (result != MA_SUCCESS) { 45898 return result; 45899 } 45900 45901 return MA_SUCCESS; 45902 } 45903 45904 MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) 45905 { 45906 if (pLPF == NULL) { 45907 return MA_INVALID_ARGS; 45908 } 45909 45910 ma_biquad_clear_cache(&pLPF->bq); 45911 45912 return MA_SUCCESS; 45913 } 45914 45915 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) 45916 { 45917 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); 45918 } 45919 45920 static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) 45921 { 45922 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); 45923 } 45924 45925 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 45926 { 45927 if (pLPF == NULL) { 45928 return MA_INVALID_ARGS; 45929 } 45930 45931 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); 45932 } 45933 45934 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) 45935 { 45936 if (pLPF == NULL) { 45937 return 0; 45938 } 45939 45940 return ma_biquad_get_latency(&pLPF->bq); 45941 } 45942 45943 45944 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 45945 { 45946 ma_lpf_config config; 45947 45948 MA_ZERO_OBJECT(&config); 45949 config.format = format; 45950 config.channels = channels; 45951 config.sampleRate = sampleRate; 45952 config.cutoffFrequency = cutoffFrequency; 45953 config.order = ma_min(order, MA_MAX_FILTER_ORDER); 45954 45955 return config; 45956 } 45957 45958 45959 typedef struct 45960 { 45961 size_t sizeInBytes; 45962 size_t lpf1Offset; 45963 size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ 45964 } ma_lpf_heap_layout; 45965 45966 static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) 45967 { 45968 MA_ASSERT(pLPF1Count != NULL); 45969 MA_ASSERT(pLPF2Count != NULL); 45970 45971 *pLPF1Count = order % 2; 45972 *pLPF2Count = order / 2; 45973 } 45974 45975 static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) 45976 { 45977 ma_result result; 45978 ma_uint32 lpf1Count; 45979 ma_uint32 lpf2Count; 45980 ma_uint32 ilpf1; 45981 ma_uint32 ilpf2; 45982 45983 MA_ASSERT(pHeapLayout != NULL); 45984 45985 MA_ZERO_OBJECT(pHeapLayout); 45986 45987 if (pConfig == NULL) { 45988 return MA_INVALID_ARGS; 45989 } 45990 45991 if (pConfig->channels == 0) { 45992 return MA_INVALID_ARGS; 45993 } 45994 45995 if (pConfig->order > MA_MAX_FILTER_ORDER) { 45996 return MA_INVALID_ARGS; 45997 } 45998 45999 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); 46000 46001 pHeapLayout->sizeInBytes = 0; 46002 46003 /* LPF 1 */ 46004 pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; 46005 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { 46006 size_t lpf1HeapSizeInBytes; 46007 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); 46008 46009 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); 46010 if (result != MA_SUCCESS) { 46011 return result; 46012 } 46013 46014 pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; 46015 } 46016 46017 /* LPF 2*/ 46018 pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; 46019 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { 46020 size_t lpf2HeapSizeInBytes; 46021 ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ 46022 46023 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); 46024 if (result != MA_SUCCESS) { 46025 return result; 46026 } 46027 46028 pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; 46029 } 46030 46031 /* Make sure allocation size is aligned. */ 46032 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 46033 46034 return MA_SUCCESS; 46035 } 46036 46037 static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) 46038 { 46039 ma_result result; 46040 ma_uint32 lpf1Count; 46041 ma_uint32 lpf2Count; 46042 ma_uint32 ilpf1; 46043 ma_uint32 ilpf2; 46044 ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ 46045 46046 if (pLPF == NULL || pConfig == NULL) { 46047 return MA_INVALID_ARGS; 46048 } 46049 46050 /* Only supporting f32 and s16. */ 46051 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 46052 return MA_INVALID_ARGS; 46053 } 46054 46055 /* The format cannot be changed after initialization. */ 46056 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { 46057 return MA_INVALID_OPERATION; 46058 } 46059 46060 /* The channel count cannot be changed after initialization. */ 46061 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { 46062 return MA_INVALID_OPERATION; 46063 } 46064 46065 if (pConfig->order > MA_MAX_FILTER_ORDER) { 46066 return MA_INVALID_ARGS; 46067 } 46068 46069 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); 46070 46071 /* The filter order can't change between reinits. */ 46072 if (!isNew) { 46073 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { 46074 return MA_INVALID_OPERATION; 46075 } 46076 } 46077 46078 if (isNew) { 46079 result = ma_lpf_get_heap_layout(pConfig, &heapLayout); 46080 if (result != MA_SUCCESS) { 46081 return result; 46082 } 46083 46084 pLPF->_pHeap = pHeap; 46085 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 46086 46087 pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); 46088 pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); 46089 } else { 46090 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ 46091 } 46092 46093 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { 46094 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); 46095 46096 if (isNew) { 46097 size_t lpf1HeapSizeInBytes; 46098 46099 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); 46100 if (result == MA_SUCCESS) { 46101 result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); 46102 } 46103 } else { 46104 result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); 46105 } 46106 46107 if (result != MA_SUCCESS) { 46108 ma_uint32 jlpf1; 46109 46110 for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { 46111 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 46112 } 46113 46114 return result; 46115 } 46116 } 46117 46118 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { 46119 ma_lpf2_config lpf2Config; 46120 double q; 46121 double a; 46122 46123 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ 46124 if (lpf1Count == 1) { 46125 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ 46126 } else { 46127 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ 46128 } 46129 q = 1 / (2*ma_cosd(a)); 46130 46131 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); 46132 46133 if (isNew) { 46134 size_t lpf2HeapSizeInBytes; 46135 46136 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); 46137 if (result == MA_SUCCESS) { 46138 result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); 46139 } 46140 } else { 46141 result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); 46142 } 46143 46144 if (result != MA_SUCCESS) { 46145 ma_uint32 jlpf1; 46146 ma_uint32 jlpf2; 46147 46148 for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { 46149 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 46150 } 46151 46152 for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { 46153 ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 46154 } 46155 46156 return result; 46157 } 46158 } 46159 46160 pLPF->lpf1Count = lpf1Count; 46161 pLPF->lpf2Count = lpf2Count; 46162 pLPF->format = pConfig->format; 46163 pLPF->channels = pConfig->channels; 46164 pLPF->sampleRate = pConfig->sampleRate; 46165 46166 return MA_SUCCESS; 46167 } 46168 46169 MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) 46170 { 46171 ma_result result; 46172 ma_lpf_heap_layout heapLayout; 46173 46174 if (pHeapSizeInBytes == NULL) { 46175 return MA_INVALID_ARGS; 46176 } 46177 46178 *pHeapSizeInBytes = 0; 46179 46180 result = ma_lpf_get_heap_layout(pConfig, &heapLayout); 46181 if (result != MA_SUCCESS) { 46182 return result; 46183 } 46184 46185 *pHeapSizeInBytes = heapLayout.sizeInBytes; 46186 46187 return result; 46188 } 46189 46190 MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) 46191 { 46192 if (pLPF == NULL) { 46193 return MA_INVALID_ARGS; 46194 } 46195 46196 MA_ZERO_OBJECT(pLPF); 46197 46198 return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); 46199 } 46200 46201 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) 46202 { 46203 ma_result result; 46204 size_t heapSizeInBytes; 46205 void* pHeap; 46206 46207 result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); 46208 if (result != MA_SUCCESS) { 46209 return result; 46210 } 46211 46212 if (heapSizeInBytes > 0) { 46213 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 46214 if (pHeap == NULL) { 46215 return MA_OUT_OF_MEMORY; 46216 } 46217 } else { 46218 pHeap = NULL; 46219 } 46220 46221 result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); 46222 if (result != MA_SUCCESS) { 46223 ma_free(pHeap, pAllocationCallbacks); 46224 return result; 46225 } 46226 46227 pLPF->_ownsHeap = MA_TRUE; 46228 return MA_SUCCESS; 46229 } 46230 46231 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) 46232 { 46233 ma_uint32 ilpf1; 46234 ma_uint32 ilpf2; 46235 46236 if (pLPF == NULL) { 46237 return; 46238 } 46239 46240 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { 46241 ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); 46242 } 46243 46244 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { 46245 ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); 46246 } 46247 46248 if (pLPF->_ownsHeap) { 46249 ma_free(pLPF->_pHeap, pAllocationCallbacks); 46250 } 46251 } 46252 46253 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) 46254 { 46255 return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); 46256 } 46257 46258 MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) 46259 { 46260 ma_uint32 ilpf1; 46261 ma_uint32 ilpf2; 46262 46263 if (pLPF == NULL) { 46264 return MA_INVALID_ARGS; 46265 } 46266 46267 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { 46268 ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); 46269 } 46270 46271 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { 46272 ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); 46273 } 46274 46275 return MA_SUCCESS; 46276 } 46277 46278 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) 46279 { 46280 ma_uint32 ilpf1; 46281 ma_uint32 ilpf2; 46282 46283 MA_ASSERT(pLPF->format == ma_format_f32); 46284 46285 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); 46286 46287 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { 46288 ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); 46289 } 46290 46291 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { 46292 ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); 46293 } 46294 } 46295 46296 static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) 46297 { 46298 ma_uint32 ilpf1; 46299 ma_uint32 ilpf2; 46300 46301 MA_ASSERT(pLPF->format == ma_format_s16); 46302 46303 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); 46304 46305 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { 46306 ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); 46307 } 46308 46309 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { 46310 ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); 46311 } 46312 } 46313 46314 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 46315 { 46316 ma_result result; 46317 ma_uint32 ilpf1; 46318 ma_uint32 ilpf2; 46319 46320 if (pLPF == NULL) { 46321 return MA_INVALID_ARGS; 46322 } 46323 46324 /* Faster path for in-place. */ 46325 if (pFramesOut == pFramesIn) { 46326 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { 46327 result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); 46328 if (result != MA_SUCCESS) { 46329 return result; 46330 } 46331 } 46332 46333 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { 46334 result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); 46335 if (result != MA_SUCCESS) { 46336 return result; 46337 } 46338 } 46339 } 46340 46341 /* Slightly slower path for copying. */ 46342 if (pFramesOut != pFramesIn) { 46343 ma_uint32 iFrame; 46344 46345 /* */ if (pLPF->format == ma_format_f32) { 46346 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 46347 const float* pFramesInF32 = (const float*)pFramesIn; 46348 46349 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 46350 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); 46351 pFramesOutF32 += pLPF->channels; 46352 pFramesInF32 += pLPF->channels; 46353 } 46354 } else if (pLPF->format == ma_format_s16) { 46355 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 46356 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 46357 46358 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 46359 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); 46360 pFramesOutS16 += pLPF->channels; 46361 pFramesInS16 += pLPF->channels; 46362 } 46363 } else { 46364 MA_ASSERT(MA_FALSE); 46365 return MA_INVALID_OPERATION; /* Should never hit this. */ 46366 } 46367 } 46368 46369 return MA_SUCCESS; 46370 } 46371 46372 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) 46373 { 46374 if (pLPF == NULL) { 46375 return 0; 46376 } 46377 46378 return pLPF->lpf2Count*2 + pLPF->lpf1Count; 46379 } 46380 46381 46382 /************************************************************************************************************************************************************** 46383 46384 High-Pass Filtering 46385 46386 **************************************************************************************************************************************************************/ 46387 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) 46388 { 46389 ma_hpf1_config config; 46390 46391 MA_ZERO_OBJECT(&config); 46392 config.format = format; 46393 config.channels = channels; 46394 config.sampleRate = sampleRate; 46395 config.cutoffFrequency = cutoffFrequency; 46396 46397 return config; 46398 } 46399 46400 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) 46401 { 46402 ma_hpf2_config config; 46403 46404 MA_ZERO_OBJECT(&config); 46405 config.format = format; 46406 config.channels = channels; 46407 config.sampleRate = sampleRate; 46408 config.cutoffFrequency = cutoffFrequency; 46409 config.q = q; 46410 46411 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ 46412 if (config.q == 0) { 46413 config.q = 0.707107; 46414 } 46415 46416 return config; 46417 } 46418 46419 46420 typedef struct 46421 { 46422 size_t sizeInBytes; 46423 size_t r1Offset; 46424 } ma_hpf1_heap_layout; 46425 46426 static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) 46427 { 46428 MA_ASSERT(pHeapLayout != NULL); 46429 46430 MA_ZERO_OBJECT(pHeapLayout); 46431 46432 if (pConfig == NULL) { 46433 return MA_INVALID_ARGS; 46434 } 46435 46436 if (pConfig->channels == 0) { 46437 return MA_INVALID_ARGS; 46438 } 46439 46440 pHeapLayout->sizeInBytes = 0; 46441 46442 /* R1 */ 46443 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; 46444 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; 46445 46446 /* Make sure allocation size is aligned. */ 46447 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 46448 46449 return MA_SUCCESS; 46450 } 46451 46452 MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) 46453 { 46454 ma_result result; 46455 ma_hpf1_heap_layout heapLayout; 46456 46457 if (pHeapSizeInBytes == NULL) { 46458 return MA_INVALID_ARGS; 46459 } 46460 46461 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); 46462 if (result != MA_SUCCESS) { 46463 return result; 46464 } 46465 46466 *pHeapSizeInBytes = heapLayout.sizeInBytes; 46467 46468 return MA_SUCCESS; 46469 } 46470 46471 MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) 46472 { 46473 ma_result result; 46474 ma_hpf1_heap_layout heapLayout; 46475 46476 if (pLPF == NULL) { 46477 return MA_INVALID_ARGS; 46478 } 46479 46480 MA_ZERO_OBJECT(pLPF); 46481 46482 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); 46483 if (result != MA_SUCCESS) { 46484 return result; 46485 } 46486 46487 pLPF->_pHeap = pHeap; 46488 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 46489 46490 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); 46491 46492 return ma_hpf1_reinit(pConfig, pLPF); 46493 } 46494 46495 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) 46496 { 46497 ma_result result; 46498 size_t heapSizeInBytes; 46499 void* pHeap; 46500 46501 result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); 46502 if (result != MA_SUCCESS) { 46503 return result; 46504 } 46505 46506 if (heapSizeInBytes > 0) { 46507 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 46508 if (pHeap == NULL) { 46509 return MA_OUT_OF_MEMORY; 46510 } 46511 } else { 46512 pHeap = NULL; 46513 } 46514 46515 result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); 46516 if (result != MA_SUCCESS) { 46517 ma_free(pHeap, pAllocationCallbacks); 46518 return result; 46519 } 46520 46521 pLPF->_ownsHeap = MA_TRUE; 46522 return MA_SUCCESS; 46523 } 46524 46525 MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) 46526 { 46527 if (pHPF == NULL) { 46528 return; 46529 } 46530 46531 if (pHPF->_ownsHeap) { 46532 ma_free(pHPF->_pHeap, pAllocationCallbacks); 46533 } 46534 } 46535 46536 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) 46537 { 46538 double a; 46539 46540 if (pHPF == NULL || pConfig == NULL) { 46541 return MA_INVALID_ARGS; 46542 } 46543 46544 /* Only supporting f32 and s16. */ 46545 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 46546 return MA_INVALID_ARGS; 46547 } 46548 46549 /* The format cannot be changed after initialization. */ 46550 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { 46551 return MA_INVALID_OPERATION; 46552 } 46553 46554 /* The channel count cannot be changed after initialization. */ 46555 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { 46556 return MA_INVALID_OPERATION; 46557 } 46558 46559 pHPF->format = pConfig->format; 46560 pHPF->channels = pConfig->channels; 46561 46562 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); 46563 if (pConfig->format == ma_format_f32) { 46564 pHPF->a.f32 = (float)a; 46565 } else { 46566 pHPF->a.s32 = ma_biquad_float_to_fp(a); 46567 } 46568 46569 return MA_SUCCESS; 46570 } 46571 46572 static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) 46573 { 46574 ma_uint32 c; 46575 const ma_uint32 channels = pHPF->channels; 46576 const float a = 1 - pHPF->a.f32; 46577 const float b = 1 - a; 46578 46579 MA_ASSUME(channels > 0); 46580 for (c = 0; c < channels; c += 1) { 46581 float r1 = pHPF->pR1[c].f32; 46582 float x = pX[c]; 46583 float y; 46584 46585 y = b*x - a*r1; 46586 46587 pY[c] = y; 46588 pHPF->pR1[c].f32 = y; 46589 } 46590 } 46591 46592 static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) 46593 { 46594 ma_uint32 c; 46595 const ma_uint32 channels = pHPF->channels; 46596 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); 46597 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); 46598 46599 MA_ASSUME(channels > 0); 46600 for (c = 0; c < channels; c += 1) { 46601 ma_int32 r1 = pHPF->pR1[c].s32; 46602 ma_int32 x = pX[c]; 46603 ma_int32 y; 46604 46605 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; 46606 46607 pY[c] = (ma_int16)y; 46608 pHPF->pR1[c].s32 = (ma_int32)y; 46609 } 46610 } 46611 46612 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 46613 { 46614 ma_uint32 n; 46615 46616 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { 46617 return MA_INVALID_ARGS; 46618 } 46619 46620 /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ 46621 46622 if (pHPF->format == ma_format_f32) { 46623 /* */ float* pY = ( float*)pFramesOut; 46624 const float* pX = (const float*)pFramesIn; 46625 46626 for (n = 0; n < frameCount; n += 1) { 46627 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); 46628 pY += pHPF->channels; 46629 pX += pHPF->channels; 46630 } 46631 } else if (pHPF->format == ma_format_s16) { 46632 /* */ ma_int16* pY = ( ma_int16*)pFramesOut; 46633 const ma_int16* pX = (const ma_int16*)pFramesIn; 46634 46635 for (n = 0; n < frameCount; n += 1) { 46636 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); 46637 pY += pHPF->channels; 46638 pX += pHPF->channels; 46639 } 46640 } else { 46641 MA_ASSERT(MA_FALSE); 46642 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ 46643 } 46644 46645 return MA_SUCCESS; 46646 } 46647 46648 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) 46649 { 46650 if (pHPF == NULL) { 46651 return 0; 46652 } 46653 46654 return 1; 46655 } 46656 46657 46658 static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) 46659 { 46660 ma_biquad_config bqConfig; 46661 double q; 46662 double w; 46663 double s; 46664 double c; 46665 double a; 46666 46667 MA_ASSERT(pConfig != NULL); 46668 46669 q = pConfig->q; 46670 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; 46671 s = ma_sind(w); 46672 c = ma_cosd(w); 46673 a = s / (2*q); 46674 46675 bqConfig.b0 = (1 + c) / 2; 46676 bqConfig.b1 = -(1 + c); 46677 bqConfig.b2 = (1 + c) / 2; 46678 bqConfig.a0 = 1 + a; 46679 bqConfig.a1 = -2 * c; 46680 bqConfig.a2 = 1 - a; 46681 46682 bqConfig.format = pConfig->format; 46683 bqConfig.channels = pConfig->channels; 46684 46685 return bqConfig; 46686 } 46687 46688 MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) 46689 { 46690 ma_biquad_config bqConfig; 46691 bqConfig = ma_hpf2__get_biquad_config(pConfig); 46692 46693 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 46694 } 46695 46696 MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) 46697 { 46698 ma_result result; 46699 ma_biquad_config bqConfig; 46700 46701 if (pHPF == NULL) { 46702 return MA_INVALID_ARGS; 46703 } 46704 46705 MA_ZERO_OBJECT(pHPF); 46706 46707 if (pConfig == NULL) { 46708 return MA_INVALID_ARGS; 46709 } 46710 46711 bqConfig = ma_hpf2__get_biquad_config(pConfig); 46712 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); 46713 if (result != MA_SUCCESS) { 46714 return result; 46715 } 46716 46717 return MA_SUCCESS; 46718 } 46719 46720 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) 46721 { 46722 ma_result result; 46723 size_t heapSizeInBytes; 46724 void* pHeap; 46725 46726 result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); 46727 if (result != MA_SUCCESS) { 46728 return result; 46729 } 46730 46731 if (heapSizeInBytes > 0) { 46732 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 46733 if (pHeap == NULL) { 46734 return MA_OUT_OF_MEMORY; 46735 } 46736 } else { 46737 pHeap = NULL; 46738 } 46739 46740 result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); 46741 if (result != MA_SUCCESS) { 46742 ma_free(pHeap, pAllocationCallbacks); 46743 return result; 46744 } 46745 46746 pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 46747 return MA_SUCCESS; 46748 } 46749 46750 MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) 46751 { 46752 if (pHPF == NULL) { 46753 return; 46754 } 46755 46756 ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 46757 } 46758 46759 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) 46760 { 46761 ma_result result; 46762 ma_biquad_config bqConfig; 46763 46764 if (pHPF == NULL || pConfig == NULL) { 46765 return MA_INVALID_ARGS; 46766 } 46767 46768 bqConfig = ma_hpf2__get_biquad_config(pConfig); 46769 result = ma_biquad_reinit(&bqConfig, &pHPF->bq); 46770 if (result != MA_SUCCESS) { 46771 return result; 46772 } 46773 46774 return MA_SUCCESS; 46775 } 46776 46777 static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) 46778 { 46779 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); 46780 } 46781 46782 static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) 46783 { 46784 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); 46785 } 46786 46787 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 46788 { 46789 if (pHPF == NULL) { 46790 return MA_INVALID_ARGS; 46791 } 46792 46793 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); 46794 } 46795 46796 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) 46797 { 46798 if (pHPF == NULL) { 46799 return 0; 46800 } 46801 46802 return ma_biquad_get_latency(&pHPF->bq); 46803 } 46804 46805 46806 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 46807 { 46808 ma_hpf_config config; 46809 46810 MA_ZERO_OBJECT(&config); 46811 config.format = format; 46812 config.channels = channels; 46813 config.sampleRate = sampleRate; 46814 config.cutoffFrequency = cutoffFrequency; 46815 config.order = ma_min(order, MA_MAX_FILTER_ORDER); 46816 46817 return config; 46818 } 46819 46820 46821 typedef struct 46822 { 46823 size_t sizeInBytes; 46824 size_t hpf1Offset; 46825 size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ 46826 } ma_hpf_heap_layout; 46827 46828 static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) 46829 { 46830 MA_ASSERT(pHPF1Count != NULL); 46831 MA_ASSERT(pHPF2Count != NULL); 46832 46833 *pHPF1Count = order % 2; 46834 *pHPF2Count = order / 2; 46835 } 46836 46837 static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) 46838 { 46839 ma_result result; 46840 ma_uint32 hpf1Count; 46841 ma_uint32 hpf2Count; 46842 ma_uint32 ihpf1; 46843 ma_uint32 ihpf2; 46844 46845 MA_ASSERT(pHeapLayout != NULL); 46846 46847 MA_ZERO_OBJECT(pHeapLayout); 46848 46849 if (pConfig == NULL) { 46850 return MA_INVALID_ARGS; 46851 } 46852 46853 if (pConfig->channels == 0) { 46854 return MA_INVALID_ARGS; 46855 } 46856 46857 if (pConfig->order > MA_MAX_FILTER_ORDER) { 46858 return MA_INVALID_ARGS; 46859 } 46860 46861 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); 46862 46863 pHeapLayout->sizeInBytes = 0; 46864 46865 /* HPF 1 */ 46866 pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; 46867 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { 46868 size_t hpf1HeapSizeInBytes; 46869 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); 46870 46871 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); 46872 if (result != MA_SUCCESS) { 46873 return result; 46874 } 46875 46876 pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; 46877 } 46878 46879 /* HPF 2*/ 46880 pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; 46881 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { 46882 size_t hpf2HeapSizeInBytes; 46883 ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ 46884 46885 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); 46886 if (result != MA_SUCCESS) { 46887 return result; 46888 } 46889 46890 pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; 46891 } 46892 46893 /* Make sure allocation size is aligned. */ 46894 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 46895 46896 return MA_SUCCESS; 46897 } 46898 46899 static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) 46900 { 46901 ma_result result; 46902 ma_uint32 hpf1Count; 46903 ma_uint32 hpf2Count; 46904 ma_uint32 ihpf1; 46905 ma_uint32 ihpf2; 46906 ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ 46907 46908 if (pHPF == NULL || pConfig == NULL) { 46909 return MA_INVALID_ARGS; 46910 } 46911 46912 /* Only supporting f32 and s16. */ 46913 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 46914 return MA_INVALID_ARGS; 46915 } 46916 46917 /* The format cannot be changed after initialization. */ 46918 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { 46919 return MA_INVALID_OPERATION; 46920 } 46921 46922 /* The channel count cannot be changed after initialization. */ 46923 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { 46924 return MA_INVALID_OPERATION; 46925 } 46926 46927 if (pConfig->order > MA_MAX_FILTER_ORDER) { 46928 return MA_INVALID_ARGS; 46929 } 46930 46931 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); 46932 46933 /* The filter order can't change between reinits. */ 46934 if (!isNew) { 46935 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { 46936 return MA_INVALID_OPERATION; 46937 } 46938 } 46939 46940 if (isNew) { 46941 result = ma_hpf_get_heap_layout(pConfig, &heapLayout); 46942 if (result != MA_SUCCESS) { 46943 return result; 46944 } 46945 46946 pHPF->_pHeap = pHeap; 46947 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 46948 46949 pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); 46950 pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); 46951 } else { 46952 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ 46953 } 46954 46955 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { 46956 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); 46957 46958 if (isNew) { 46959 size_t hpf1HeapSizeInBytes; 46960 46961 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); 46962 if (result == MA_SUCCESS) { 46963 result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); 46964 } 46965 } else { 46966 result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); 46967 } 46968 46969 if (result != MA_SUCCESS) { 46970 ma_uint32 jhpf1; 46971 46972 for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { 46973 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 46974 } 46975 46976 return result; 46977 } 46978 } 46979 46980 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { 46981 ma_hpf2_config hpf2Config; 46982 double q; 46983 double a; 46984 46985 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ 46986 if (hpf1Count == 1) { 46987 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ 46988 } else { 46989 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ 46990 } 46991 q = 1 / (2*ma_cosd(a)); 46992 46993 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); 46994 46995 if (isNew) { 46996 size_t hpf2HeapSizeInBytes; 46997 46998 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); 46999 if (result == MA_SUCCESS) { 47000 result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); 47001 } 47002 } else { 47003 result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); 47004 } 47005 47006 if (result != MA_SUCCESS) { 47007 ma_uint32 jhpf1; 47008 ma_uint32 jhpf2; 47009 47010 for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { 47011 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 47012 } 47013 47014 for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { 47015 ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ 47016 } 47017 47018 return result; 47019 } 47020 } 47021 47022 pHPF->hpf1Count = hpf1Count; 47023 pHPF->hpf2Count = hpf2Count; 47024 pHPF->format = pConfig->format; 47025 pHPF->channels = pConfig->channels; 47026 pHPF->sampleRate = pConfig->sampleRate; 47027 47028 return MA_SUCCESS; 47029 } 47030 47031 MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) 47032 { 47033 ma_result result; 47034 ma_hpf_heap_layout heapLayout; 47035 47036 if (pHeapSizeInBytes == NULL) { 47037 return MA_INVALID_ARGS; 47038 } 47039 47040 *pHeapSizeInBytes = 0; 47041 47042 result = ma_hpf_get_heap_layout(pConfig, &heapLayout); 47043 if (result != MA_SUCCESS) { 47044 return result; 47045 } 47046 47047 *pHeapSizeInBytes = heapLayout.sizeInBytes; 47048 47049 return result; 47050 } 47051 47052 MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) 47053 { 47054 if (pLPF == NULL) { 47055 return MA_INVALID_ARGS; 47056 } 47057 47058 MA_ZERO_OBJECT(pLPF); 47059 47060 return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); 47061 } 47062 47063 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) 47064 { 47065 ma_result result; 47066 size_t heapSizeInBytes; 47067 void* pHeap; 47068 47069 result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); 47070 if (result != MA_SUCCESS) { 47071 return result; 47072 } 47073 47074 if (heapSizeInBytes > 0) { 47075 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 47076 if (pHeap == NULL) { 47077 return MA_OUT_OF_MEMORY; 47078 } 47079 } else { 47080 pHeap = NULL; 47081 } 47082 47083 result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); 47084 if (result != MA_SUCCESS) { 47085 ma_free(pHeap, pAllocationCallbacks); 47086 return result; 47087 } 47088 47089 pHPF->_ownsHeap = MA_TRUE; 47090 return MA_SUCCESS; 47091 } 47092 47093 MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) 47094 { 47095 ma_uint32 ihpf1; 47096 ma_uint32 ihpf2; 47097 47098 if (pHPF == NULL) { 47099 return; 47100 } 47101 47102 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { 47103 ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); 47104 } 47105 47106 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { 47107 ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); 47108 } 47109 47110 if (pHPF->_ownsHeap) { 47111 ma_free(pHPF->_pHeap, pAllocationCallbacks); 47112 } 47113 } 47114 47115 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) 47116 { 47117 return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); 47118 } 47119 47120 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 47121 { 47122 ma_result result; 47123 ma_uint32 ihpf1; 47124 ma_uint32 ihpf2; 47125 47126 if (pHPF == NULL) { 47127 return MA_INVALID_ARGS; 47128 } 47129 47130 /* Faster path for in-place. */ 47131 if (pFramesOut == pFramesIn) { 47132 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { 47133 result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); 47134 if (result != MA_SUCCESS) { 47135 return result; 47136 } 47137 } 47138 47139 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { 47140 result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); 47141 if (result != MA_SUCCESS) { 47142 return result; 47143 } 47144 } 47145 } 47146 47147 /* Slightly slower path for copying. */ 47148 if (pFramesOut != pFramesIn) { 47149 ma_uint32 iFrame; 47150 47151 /* */ if (pHPF->format == ma_format_f32) { 47152 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 47153 const float* pFramesInF32 = (const float*)pFramesIn; 47154 47155 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 47156 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); 47157 47158 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { 47159 ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); 47160 } 47161 47162 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { 47163 ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); 47164 } 47165 47166 pFramesOutF32 += pHPF->channels; 47167 pFramesInF32 += pHPF->channels; 47168 } 47169 } else if (pHPF->format == ma_format_s16) { 47170 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 47171 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 47172 47173 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 47174 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); 47175 47176 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { 47177 ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); 47178 } 47179 47180 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { 47181 ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); 47182 } 47183 47184 pFramesOutS16 += pHPF->channels; 47185 pFramesInS16 += pHPF->channels; 47186 } 47187 } else { 47188 MA_ASSERT(MA_FALSE); 47189 return MA_INVALID_OPERATION; /* Should never hit this. */ 47190 } 47191 } 47192 47193 return MA_SUCCESS; 47194 } 47195 47196 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) 47197 { 47198 if (pHPF == NULL) { 47199 return 0; 47200 } 47201 47202 return pHPF->hpf2Count*2 + pHPF->hpf1Count; 47203 } 47204 47205 47206 /************************************************************************************************************************************************************** 47207 47208 Band-Pass Filtering 47209 47210 **************************************************************************************************************************************************************/ 47211 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) 47212 { 47213 ma_bpf2_config config; 47214 47215 MA_ZERO_OBJECT(&config); 47216 config.format = format; 47217 config.channels = channels; 47218 config.sampleRate = sampleRate; 47219 config.cutoffFrequency = cutoffFrequency; 47220 config.q = q; 47221 47222 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ 47223 if (config.q == 0) { 47224 config.q = 0.707107; 47225 } 47226 47227 return config; 47228 } 47229 47230 47231 static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) 47232 { 47233 ma_biquad_config bqConfig; 47234 double q; 47235 double w; 47236 double s; 47237 double c; 47238 double a; 47239 47240 MA_ASSERT(pConfig != NULL); 47241 47242 q = pConfig->q; 47243 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; 47244 s = ma_sind(w); 47245 c = ma_cosd(w); 47246 a = s / (2*q); 47247 47248 bqConfig.b0 = q * a; 47249 bqConfig.b1 = 0; 47250 bqConfig.b2 = -q * a; 47251 bqConfig.a0 = 1 + a; 47252 bqConfig.a1 = -2 * c; 47253 bqConfig.a2 = 1 - a; 47254 47255 bqConfig.format = pConfig->format; 47256 bqConfig.channels = pConfig->channels; 47257 47258 return bqConfig; 47259 } 47260 47261 MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) 47262 { 47263 ma_biquad_config bqConfig; 47264 bqConfig = ma_bpf2__get_biquad_config(pConfig); 47265 47266 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 47267 } 47268 47269 MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) 47270 { 47271 ma_result result; 47272 ma_biquad_config bqConfig; 47273 47274 if (pBPF == NULL) { 47275 return MA_INVALID_ARGS; 47276 } 47277 47278 MA_ZERO_OBJECT(pBPF); 47279 47280 if (pConfig == NULL) { 47281 return MA_INVALID_ARGS; 47282 } 47283 47284 bqConfig = ma_bpf2__get_biquad_config(pConfig); 47285 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); 47286 if (result != MA_SUCCESS) { 47287 return result; 47288 } 47289 47290 return MA_SUCCESS; 47291 } 47292 47293 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) 47294 { 47295 ma_result result; 47296 size_t heapSizeInBytes; 47297 void* pHeap; 47298 47299 result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); 47300 if (result != MA_SUCCESS) { 47301 return result; 47302 } 47303 47304 if (heapSizeInBytes > 0) { 47305 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 47306 if (pHeap == NULL) { 47307 return MA_OUT_OF_MEMORY; 47308 } 47309 } else { 47310 pHeap = NULL; 47311 } 47312 47313 result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); 47314 if (result != MA_SUCCESS) { 47315 ma_free(pHeap, pAllocationCallbacks); 47316 return result; 47317 } 47318 47319 pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 47320 return MA_SUCCESS; 47321 } 47322 47323 MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) 47324 { 47325 if (pBPF == NULL) { 47326 return; 47327 } 47328 47329 ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 47330 } 47331 47332 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) 47333 { 47334 ma_result result; 47335 ma_biquad_config bqConfig; 47336 47337 if (pBPF == NULL || pConfig == NULL) { 47338 return MA_INVALID_ARGS; 47339 } 47340 47341 bqConfig = ma_bpf2__get_biquad_config(pConfig); 47342 result = ma_biquad_reinit(&bqConfig, &pBPF->bq); 47343 if (result != MA_SUCCESS) { 47344 return result; 47345 } 47346 47347 return MA_SUCCESS; 47348 } 47349 47350 static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) 47351 { 47352 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); 47353 } 47354 47355 static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) 47356 { 47357 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); 47358 } 47359 47360 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 47361 { 47362 if (pBPF == NULL) { 47363 return MA_INVALID_ARGS; 47364 } 47365 47366 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); 47367 } 47368 47369 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) 47370 { 47371 if (pBPF == NULL) { 47372 return 0; 47373 } 47374 47375 return ma_biquad_get_latency(&pBPF->bq); 47376 } 47377 47378 47379 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 47380 { 47381 ma_bpf_config config; 47382 47383 MA_ZERO_OBJECT(&config); 47384 config.format = format; 47385 config.channels = channels; 47386 config.sampleRate = sampleRate; 47387 config.cutoffFrequency = cutoffFrequency; 47388 config.order = ma_min(order, MA_MAX_FILTER_ORDER); 47389 47390 return config; 47391 } 47392 47393 47394 typedef struct 47395 { 47396 size_t sizeInBytes; 47397 size_t bpf2Offset; 47398 } ma_bpf_heap_layout; 47399 47400 static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) 47401 { 47402 ma_result result; 47403 ma_uint32 bpf2Count; 47404 ma_uint32 ibpf2; 47405 47406 MA_ASSERT(pHeapLayout != NULL); 47407 47408 MA_ZERO_OBJECT(pHeapLayout); 47409 47410 if (pConfig == NULL) { 47411 return MA_INVALID_ARGS; 47412 } 47413 47414 if (pConfig->order > MA_MAX_FILTER_ORDER) { 47415 return MA_INVALID_ARGS; 47416 } 47417 47418 /* We must have an even number of order. */ 47419 if ((pConfig->order & 0x1) != 0) { 47420 return MA_INVALID_ARGS; 47421 } 47422 47423 bpf2Count = pConfig->channels / 2; 47424 47425 pHeapLayout->sizeInBytes = 0; 47426 47427 /* BPF 2 */ 47428 pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; 47429 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { 47430 size_t bpf2HeapSizeInBytes; 47431 ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ 47432 47433 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); 47434 if (result != MA_SUCCESS) { 47435 return result; 47436 } 47437 47438 pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; 47439 } 47440 47441 /* Make sure allocation size is aligned. */ 47442 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 47443 47444 return MA_SUCCESS; 47445 } 47446 47447 static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) 47448 { 47449 ma_result result; 47450 ma_uint32 bpf2Count; 47451 ma_uint32 ibpf2; 47452 ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ 47453 47454 if (pBPF == NULL || pConfig == NULL) { 47455 return MA_INVALID_ARGS; 47456 } 47457 47458 /* Only supporting f32 and s16. */ 47459 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 47460 return MA_INVALID_ARGS; 47461 } 47462 47463 /* The format cannot be changed after initialization. */ 47464 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { 47465 return MA_INVALID_OPERATION; 47466 } 47467 47468 /* The channel count cannot be changed after initialization. */ 47469 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { 47470 return MA_INVALID_OPERATION; 47471 } 47472 47473 if (pConfig->order > MA_MAX_FILTER_ORDER) { 47474 return MA_INVALID_ARGS; 47475 } 47476 47477 /* We must have an even number of order. */ 47478 if ((pConfig->order & 0x1) != 0) { 47479 return MA_INVALID_ARGS; 47480 } 47481 47482 bpf2Count = pConfig->order / 2; 47483 47484 /* The filter order can't change between reinits. */ 47485 if (!isNew) { 47486 if (pBPF->bpf2Count != bpf2Count) { 47487 return MA_INVALID_OPERATION; 47488 } 47489 } 47490 47491 if (isNew) { 47492 result = ma_bpf_get_heap_layout(pConfig, &heapLayout); 47493 if (result != MA_SUCCESS) { 47494 return result; 47495 } 47496 47497 pBPF->_pHeap = pHeap; 47498 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 47499 47500 pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); 47501 } else { 47502 MA_ZERO_OBJECT(&heapLayout); 47503 } 47504 47505 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { 47506 ma_bpf2_config bpf2Config; 47507 double q; 47508 47509 /* TODO: Calculate Q to make this a proper Butterworth filter. */ 47510 q = 0.707107; 47511 47512 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); 47513 47514 if (isNew) { 47515 size_t bpf2HeapSizeInBytes; 47516 47517 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); 47518 if (result == MA_SUCCESS) { 47519 result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); 47520 } 47521 } else { 47522 result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); 47523 } 47524 47525 if (result != MA_SUCCESS) { 47526 return result; 47527 } 47528 } 47529 47530 pBPF->bpf2Count = bpf2Count; 47531 pBPF->format = pConfig->format; 47532 pBPF->channels = pConfig->channels; 47533 47534 return MA_SUCCESS; 47535 } 47536 47537 47538 MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) 47539 { 47540 ma_result result; 47541 ma_bpf_heap_layout heapLayout; 47542 47543 if (pHeapSizeInBytes == NULL) { 47544 return MA_INVALID_ARGS; 47545 } 47546 47547 *pHeapSizeInBytes = 0; 47548 47549 result = ma_bpf_get_heap_layout(pConfig, &heapLayout); 47550 if (result != MA_SUCCESS) { 47551 return result; 47552 } 47553 47554 *pHeapSizeInBytes = heapLayout.sizeInBytes; 47555 47556 return MA_SUCCESS; 47557 } 47558 47559 MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) 47560 { 47561 if (pBPF == NULL) { 47562 return MA_INVALID_ARGS; 47563 } 47564 47565 MA_ZERO_OBJECT(pBPF); 47566 47567 return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); 47568 } 47569 47570 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) 47571 { 47572 ma_result result; 47573 size_t heapSizeInBytes; 47574 void* pHeap; 47575 47576 result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); 47577 if (result != MA_SUCCESS) { 47578 return result; 47579 } 47580 47581 if (heapSizeInBytes > 0) { 47582 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 47583 if (pHeap == NULL) { 47584 return MA_OUT_OF_MEMORY; 47585 } 47586 } else { 47587 pHeap = NULL; 47588 } 47589 47590 result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); 47591 if (result != MA_SUCCESS) { 47592 ma_free(pHeap, pAllocationCallbacks); 47593 return result; 47594 } 47595 47596 pBPF->_ownsHeap = MA_TRUE; 47597 return MA_SUCCESS; 47598 } 47599 47600 MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) 47601 { 47602 ma_uint32 ibpf2; 47603 47604 if (pBPF == NULL) { 47605 return; 47606 } 47607 47608 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { 47609 ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); 47610 } 47611 47612 if (pBPF->_ownsHeap) { 47613 ma_free(pBPF->_pHeap, pAllocationCallbacks); 47614 } 47615 } 47616 47617 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) 47618 { 47619 return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); 47620 } 47621 47622 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 47623 { 47624 ma_result result; 47625 ma_uint32 ibpf2; 47626 47627 if (pBPF == NULL) { 47628 return MA_INVALID_ARGS; 47629 } 47630 47631 /* Faster path for in-place. */ 47632 if (pFramesOut == pFramesIn) { 47633 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { 47634 result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); 47635 if (result != MA_SUCCESS) { 47636 return result; 47637 } 47638 } 47639 } 47640 47641 /* Slightly slower path for copying. */ 47642 if (pFramesOut != pFramesIn) { 47643 ma_uint32 iFrame; 47644 47645 /* */ if (pBPF->format == ma_format_f32) { 47646 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 47647 const float* pFramesInF32 = (const float*)pFramesIn; 47648 47649 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 47650 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); 47651 47652 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { 47653 ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); 47654 } 47655 47656 pFramesOutF32 += pBPF->channels; 47657 pFramesInF32 += pBPF->channels; 47658 } 47659 } else if (pBPF->format == ma_format_s16) { 47660 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 47661 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 47662 47663 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 47664 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); 47665 47666 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { 47667 ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); 47668 } 47669 47670 pFramesOutS16 += pBPF->channels; 47671 pFramesInS16 += pBPF->channels; 47672 } 47673 } else { 47674 MA_ASSERT(MA_FALSE); 47675 return MA_INVALID_OPERATION; /* Should never hit this. */ 47676 } 47677 } 47678 47679 return MA_SUCCESS; 47680 } 47681 47682 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) 47683 { 47684 if (pBPF == NULL) { 47685 return 0; 47686 } 47687 47688 return pBPF->bpf2Count*2; 47689 } 47690 47691 47692 /************************************************************************************************************************************************************** 47693 47694 Notching Filter 47695 47696 **************************************************************************************************************************************************************/ 47697 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) 47698 { 47699 ma_notch2_config config; 47700 47701 MA_ZERO_OBJECT(&config); 47702 config.format = format; 47703 config.channels = channels; 47704 config.sampleRate = sampleRate; 47705 config.q = q; 47706 config.frequency = frequency; 47707 47708 if (config.q == 0) { 47709 config.q = 0.707107; 47710 } 47711 47712 return config; 47713 } 47714 47715 47716 static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) 47717 { 47718 ma_biquad_config bqConfig; 47719 double q; 47720 double w; 47721 double s; 47722 double c; 47723 double a; 47724 47725 MA_ASSERT(pConfig != NULL); 47726 47727 q = pConfig->q; 47728 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; 47729 s = ma_sind(w); 47730 c = ma_cosd(w); 47731 a = s / (2*q); 47732 47733 bqConfig.b0 = 1; 47734 bqConfig.b1 = -2 * c; 47735 bqConfig.b2 = 1; 47736 bqConfig.a0 = 1 + a; 47737 bqConfig.a1 = -2 * c; 47738 bqConfig.a2 = 1 - a; 47739 47740 bqConfig.format = pConfig->format; 47741 bqConfig.channels = pConfig->channels; 47742 47743 return bqConfig; 47744 } 47745 47746 MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) 47747 { 47748 ma_biquad_config bqConfig; 47749 bqConfig = ma_notch2__get_biquad_config(pConfig); 47750 47751 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 47752 } 47753 47754 MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) 47755 { 47756 ma_result result; 47757 ma_biquad_config bqConfig; 47758 47759 if (pFilter == NULL) { 47760 return MA_INVALID_ARGS; 47761 } 47762 47763 MA_ZERO_OBJECT(pFilter); 47764 47765 if (pConfig == NULL) { 47766 return MA_INVALID_ARGS; 47767 } 47768 47769 bqConfig = ma_notch2__get_biquad_config(pConfig); 47770 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); 47771 if (result != MA_SUCCESS) { 47772 return result; 47773 } 47774 47775 return MA_SUCCESS; 47776 } 47777 47778 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) 47779 { 47780 ma_result result; 47781 size_t heapSizeInBytes; 47782 void* pHeap; 47783 47784 result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); 47785 if (result != MA_SUCCESS) { 47786 return result; 47787 } 47788 47789 if (heapSizeInBytes > 0) { 47790 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 47791 if (pHeap == NULL) { 47792 return MA_OUT_OF_MEMORY; 47793 } 47794 } else { 47795 pHeap = NULL; 47796 } 47797 47798 result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); 47799 if (result != MA_SUCCESS) { 47800 ma_free(pHeap, pAllocationCallbacks); 47801 return result; 47802 } 47803 47804 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 47805 return MA_SUCCESS; 47806 } 47807 47808 MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) 47809 { 47810 if (pFilter == NULL) { 47811 return; 47812 } 47813 47814 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 47815 } 47816 47817 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) 47818 { 47819 ma_result result; 47820 ma_biquad_config bqConfig; 47821 47822 if (pFilter == NULL || pConfig == NULL) { 47823 return MA_INVALID_ARGS; 47824 } 47825 47826 bqConfig = ma_notch2__get_biquad_config(pConfig); 47827 result = ma_biquad_reinit(&bqConfig, &pFilter->bq); 47828 if (result != MA_SUCCESS) { 47829 return result; 47830 } 47831 47832 return MA_SUCCESS; 47833 } 47834 47835 static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) 47836 { 47837 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); 47838 } 47839 47840 static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) 47841 { 47842 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); 47843 } 47844 47845 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 47846 { 47847 if (pFilter == NULL) { 47848 return MA_INVALID_ARGS; 47849 } 47850 47851 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); 47852 } 47853 47854 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) 47855 { 47856 if (pFilter == NULL) { 47857 return 0; 47858 } 47859 47860 return ma_biquad_get_latency(&pFilter->bq); 47861 } 47862 47863 47864 47865 /************************************************************************************************************************************************************** 47866 47867 Peaking EQ Filter 47868 47869 **************************************************************************************************************************************************************/ 47870 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) 47871 { 47872 ma_peak2_config config; 47873 47874 MA_ZERO_OBJECT(&config); 47875 config.format = format; 47876 config.channels = channels; 47877 config.sampleRate = sampleRate; 47878 config.gainDB = gainDB; 47879 config.q = q; 47880 config.frequency = frequency; 47881 47882 if (config.q == 0) { 47883 config.q = 0.707107; 47884 } 47885 47886 return config; 47887 } 47888 47889 47890 static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) 47891 { 47892 ma_biquad_config bqConfig; 47893 double q; 47894 double w; 47895 double s; 47896 double c; 47897 double a; 47898 double A; 47899 47900 MA_ASSERT(pConfig != NULL); 47901 47902 q = pConfig->q; 47903 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; 47904 s = ma_sind(w); 47905 c = ma_cosd(w); 47906 a = s / (2*q); 47907 A = ma_powd(10, (pConfig->gainDB / 40)); 47908 47909 bqConfig.b0 = 1 + (a * A); 47910 bqConfig.b1 = -2 * c; 47911 bqConfig.b2 = 1 - (a * A); 47912 bqConfig.a0 = 1 + (a / A); 47913 bqConfig.a1 = -2 * c; 47914 bqConfig.a2 = 1 - (a / A); 47915 47916 bqConfig.format = pConfig->format; 47917 bqConfig.channels = pConfig->channels; 47918 47919 return bqConfig; 47920 } 47921 47922 MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) 47923 { 47924 ma_biquad_config bqConfig; 47925 bqConfig = ma_peak2__get_biquad_config(pConfig); 47926 47927 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 47928 } 47929 47930 MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) 47931 { 47932 ma_result result; 47933 ma_biquad_config bqConfig; 47934 47935 if (pFilter == NULL) { 47936 return MA_INVALID_ARGS; 47937 } 47938 47939 MA_ZERO_OBJECT(pFilter); 47940 47941 if (pConfig == NULL) { 47942 return MA_INVALID_ARGS; 47943 } 47944 47945 bqConfig = ma_peak2__get_biquad_config(pConfig); 47946 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); 47947 if (result != MA_SUCCESS) { 47948 return result; 47949 } 47950 47951 return MA_SUCCESS; 47952 } 47953 47954 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) 47955 { 47956 ma_result result; 47957 size_t heapSizeInBytes; 47958 void* pHeap; 47959 47960 result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); 47961 if (result != MA_SUCCESS) { 47962 return result; 47963 } 47964 47965 if (heapSizeInBytes > 0) { 47966 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 47967 if (pHeap == NULL) { 47968 return MA_OUT_OF_MEMORY; 47969 } 47970 } else { 47971 pHeap = NULL; 47972 } 47973 47974 result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); 47975 if (result != MA_SUCCESS) { 47976 ma_free(pHeap, pAllocationCallbacks); 47977 return result; 47978 } 47979 47980 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 47981 return MA_SUCCESS; 47982 } 47983 47984 MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) 47985 { 47986 if (pFilter == NULL) { 47987 return; 47988 } 47989 47990 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 47991 } 47992 47993 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) 47994 { 47995 ma_result result; 47996 ma_biquad_config bqConfig; 47997 47998 if (pFilter == NULL || pConfig == NULL) { 47999 return MA_INVALID_ARGS; 48000 } 48001 48002 bqConfig = ma_peak2__get_biquad_config(pConfig); 48003 result = ma_biquad_reinit(&bqConfig, &pFilter->bq); 48004 if (result != MA_SUCCESS) { 48005 return result; 48006 } 48007 48008 return MA_SUCCESS; 48009 } 48010 48011 static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) 48012 { 48013 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); 48014 } 48015 48016 static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) 48017 { 48018 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); 48019 } 48020 48021 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 48022 { 48023 if (pFilter == NULL) { 48024 return MA_INVALID_ARGS; 48025 } 48026 48027 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); 48028 } 48029 48030 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) 48031 { 48032 if (pFilter == NULL) { 48033 return 0; 48034 } 48035 48036 return ma_biquad_get_latency(&pFilter->bq); 48037 } 48038 48039 48040 /************************************************************************************************************************************************************** 48041 48042 Low Shelf Filter 48043 48044 **************************************************************************************************************************************************************/ 48045 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) 48046 { 48047 ma_loshelf2_config config; 48048 48049 MA_ZERO_OBJECT(&config); 48050 config.format = format; 48051 config.channels = channels; 48052 config.sampleRate = sampleRate; 48053 config.gainDB = gainDB; 48054 config.shelfSlope = shelfSlope; 48055 config.frequency = frequency; 48056 48057 return config; 48058 } 48059 48060 48061 static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) 48062 { 48063 ma_biquad_config bqConfig; 48064 double w; 48065 double s; 48066 double c; 48067 double A; 48068 double S; 48069 double a; 48070 double sqrtA; 48071 48072 MA_ASSERT(pConfig != NULL); 48073 48074 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; 48075 s = ma_sind(w); 48076 c = ma_cosd(w); 48077 A = ma_powd(10, (pConfig->gainDB / 40)); 48078 S = pConfig->shelfSlope; 48079 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); 48080 sqrtA = 2*ma_sqrtd(A)*a; 48081 48082 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); 48083 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); 48084 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); 48085 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; 48086 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); 48087 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; 48088 48089 bqConfig.format = pConfig->format; 48090 bqConfig.channels = pConfig->channels; 48091 48092 return bqConfig; 48093 } 48094 48095 MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) 48096 { 48097 ma_biquad_config bqConfig; 48098 bqConfig = ma_loshelf2__get_biquad_config(pConfig); 48099 48100 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 48101 } 48102 48103 MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) 48104 { 48105 ma_result result; 48106 ma_biquad_config bqConfig; 48107 48108 if (pFilter == NULL) { 48109 return MA_INVALID_ARGS; 48110 } 48111 48112 MA_ZERO_OBJECT(pFilter); 48113 48114 if (pConfig == NULL) { 48115 return MA_INVALID_ARGS; 48116 } 48117 48118 bqConfig = ma_loshelf2__get_biquad_config(pConfig); 48119 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); 48120 if (result != MA_SUCCESS) { 48121 return result; 48122 } 48123 48124 return MA_SUCCESS; 48125 } 48126 48127 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) 48128 { 48129 ma_result result; 48130 size_t heapSizeInBytes; 48131 void* pHeap; 48132 48133 result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); 48134 if (result != MA_SUCCESS) { 48135 return result; 48136 } 48137 48138 if (heapSizeInBytes > 0) { 48139 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 48140 if (pHeap == NULL) { 48141 return MA_OUT_OF_MEMORY; 48142 } 48143 } else { 48144 pHeap = NULL; 48145 } 48146 48147 result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); 48148 if (result != MA_SUCCESS) { 48149 ma_free(pHeap, pAllocationCallbacks); 48150 return result; 48151 } 48152 48153 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 48154 return MA_SUCCESS; 48155 } 48156 48157 MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) 48158 { 48159 if (pFilter == NULL) { 48160 return; 48161 } 48162 48163 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 48164 } 48165 48166 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) 48167 { 48168 ma_result result; 48169 ma_biquad_config bqConfig; 48170 48171 if (pFilter == NULL || pConfig == NULL) { 48172 return MA_INVALID_ARGS; 48173 } 48174 48175 bqConfig = ma_loshelf2__get_biquad_config(pConfig); 48176 result = ma_biquad_reinit(&bqConfig, &pFilter->bq); 48177 if (result != MA_SUCCESS) { 48178 return result; 48179 } 48180 48181 return MA_SUCCESS; 48182 } 48183 48184 static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) 48185 { 48186 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); 48187 } 48188 48189 static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) 48190 { 48191 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); 48192 } 48193 48194 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 48195 { 48196 if (pFilter == NULL) { 48197 return MA_INVALID_ARGS; 48198 } 48199 48200 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); 48201 } 48202 48203 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) 48204 { 48205 if (pFilter == NULL) { 48206 return 0; 48207 } 48208 48209 return ma_biquad_get_latency(&pFilter->bq); 48210 } 48211 48212 48213 /************************************************************************************************************************************************************** 48214 48215 High Shelf Filter 48216 48217 **************************************************************************************************************************************************************/ 48218 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) 48219 { 48220 ma_hishelf2_config config; 48221 48222 MA_ZERO_OBJECT(&config); 48223 config.format = format; 48224 config.channels = channels; 48225 config.sampleRate = sampleRate; 48226 config.gainDB = gainDB; 48227 config.shelfSlope = shelfSlope; 48228 config.frequency = frequency; 48229 48230 return config; 48231 } 48232 48233 48234 static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) 48235 { 48236 ma_biquad_config bqConfig; 48237 double w; 48238 double s; 48239 double c; 48240 double A; 48241 double S; 48242 double a; 48243 double sqrtA; 48244 48245 MA_ASSERT(pConfig != NULL); 48246 48247 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; 48248 s = ma_sind(w); 48249 c = ma_cosd(w); 48250 A = ma_powd(10, (pConfig->gainDB / 40)); 48251 S = pConfig->shelfSlope; 48252 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); 48253 sqrtA = 2*ma_sqrtd(A)*a; 48254 48255 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); 48256 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); 48257 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); 48258 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; 48259 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); 48260 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; 48261 48262 bqConfig.format = pConfig->format; 48263 bqConfig.channels = pConfig->channels; 48264 48265 return bqConfig; 48266 } 48267 48268 MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) 48269 { 48270 ma_biquad_config bqConfig; 48271 bqConfig = ma_hishelf2__get_biquad_config(pConfig); 48272 48273 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); 48274 } 48275 48276 MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) 48277 { 48278 ma_result result; 48279 ma_biquad_config bqConfig; 48280 48281 if (pFilter == NULL) { 48282 return MA_INVALID_ARGS; 48283 } 48284 48285 MA_ZERO_OBJECT(pFilter); 48286 48287 if (pConfig == NULL) { 48288 return MA_INVALID_ARGS; 48289 } 48290 48291 bqConfig = ma_hishelf2__get_biquad_config(pConfig); 48292 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); 48293 if (result != MA_SUCCESS) { 48294 return result; 48295 } 48296 48297 return MA_SUCCESS; 48298 } 48299 48300 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) 48301 { 48302 ma_result result; 48303 size_t heapSizeInBytes; 48304 void* pHeap; 48305 48306 result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); 48307 if (result != MA_SUCCESS) { 48308 return result; 48309 } 48310 48311 if (heapSizeInBytes > 0) { 48312 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 48313 if (pHeap == NULL) { 48314 return MA_OUT_OF_MEMORY; 48315 } 48316 } else { 48317 pHeap = NULL; 48318 } 48319 48320 result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); 48321 if (result != MA_SUCCESS) { 48322 ma_free(pHeap, pAllocationCallbacks); 48323 return result; 48324 } 48325 48326 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ 48327 return MA_SUCCESS; 48328 } 48329 48330 MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) 48331 { 48332 if (pFilter == NULL) { 48333 return; 48334 } 48335 48336 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ 48337 } 48338 48339 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) 48340 { 48341 ma_result result; 48342 ma_biquad_config bqConfig; 48343 48344 if (pFilter == NULL || pConfig == NULL) { 48345 return MA_INVALID_ARGS; 48346 } 48347 48348 bqConfig = ma_hishelf2__get_biquad_config(pConfig); 48349 result = ma_biquad_reinit(&bqConfig, &pFilter->bq); 48350 if (result != MA_SUCCESS) { 48351 return result; 48352 } 48353 48354 return MA_SUCCESS; 48355 } 48356 48357 static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) 48358 { 48359 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); 48360 } 48361 48362 static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) 48363 { 48364 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); 48365 } 48366 48367 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 48368 { 48369 if (pFilter == NULL) { 48370 return MA_INVALID_ARGS; 48371 } 48372 48373 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); 48374 } 48375 48376 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) 48377 { 48378 if (pFilter == NULL) { 48379 return 0; 48380 } 48381 48382 return ma_biquad_get_latency(&pFilter->bq); 48383 } 48384 48385 48386 48387 /* 48388 Delay 48389 */ 48390 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) 48391 { 48392 ma_delay_config config; 48393 48394 MA_ZERO_OBJECT(&config); 48395 config.channels = channels; 48396 config.sampleRate = sampleRate; 48397 config.delayInFrames = delayInFrames; 48398 config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ 48399 config.wet = 1; 48400 config.dry = 1; 48401 config.decay = decay; 48402 48403 return config; 48404 } 48405 48406 48407 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) 48408 { 48409 if (pDelay == NULL) { 48410 return MA_INVALID_ARGS; 48411 } 48412 48413 MA_ZERO_OBJECT(pDelay); 48414 48415 if (pConfig == NULL) { 48416 return MA_INVALID_ARGS; 48417 } 48418 48419 if (pConfig->decay < 0 || pConfig->decay > 1) { 48420 return MA_INVALID_ARGS; 48421 } 48422 48423 pDelay->config = *pConfig; 48424 pDelay->bufferSizeInFrames = pConfig->delayInFrames; 48425 pDelay->cursor = 0; 48426 48427 pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); 48428 if (pDelay->pBuffer == NULL) { 48429 return MA_OUT_OF_MEMORY; 48430 } 48431 48432 ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); 48433 48434 return MA_SUCCESS; 48435 } 48436 48437 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) 48438 { 48439 if (pDelay == NULL) { 48440 return; 48441 } 48442 48443 ma_free(pDelay->pBuffer, pAllocationCallbacks); 48444 } 48445 48446 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) 48447 { 48448 ma_uint32 iFrame; 48449 ma_uint32 iChannel; 48450 float* pFramesOutF32 = (float*)pFramesOut; 48451 const float* pFramesInF32 = (const float*)pFramesIn; 48452 48453 if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { 48454 return MA_INVALID_ARGS; 48455 } 48456 48457 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 48458 for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { 48459 ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; 48460 48461 if (pDelay->config.delayStart) { 48462 /* Delayed start. */ 48463 48464 /* Read */ 48465 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; 48466 48467 /* Feedback */ 48468 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); 48469 } else { 48470 /* Immediate start */ 48471 48472 /* Feedback */ 48473 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); 48474 48475 /* Read */ 48476 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; 48477 } 48478 } 48479 48480 pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; 48481 48482 pFramesOutF32 += pDelay->config.channels; 48483 pFramesInF32 += pDelay->config.channels; 48484 } 48485 48486 return MA_SUCCESS; 48487 } 48488 48489 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) 48490 { 48491 if (pDelay == NULL) { 48492 return; 48493 } 48494 48495 pDelay->config.wet = value; 48496 } 48497 48498 MA_API float ma_delay_get_wet(const ma_delay* pDelay) 48499 { 48500 if (pDelay == NULL) { 48501 return 0; 48502 } 48503 48504 return pDelay->config.wet; 48505 } 48506 48507 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) 48508 { 48509 if (pDelay == NULL) { 48510 return; 48511 } 48512 48513 pDelay->config.dry = value; 48514 } 48515 48516 MA_API float ma_delay_get_dry(const ma_delay* pDelay) 48517 { 48518 if (pDelay == NULL) { 48519 return 0; 48520 } 48521 48522 return pDelay->config.dry; 48523 } 48524 48525 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) 48526 { 48527 if (pDelay == NULL) { 48528 return; 48529 } 48530 48531 pDelay->config.decay = value; 48532 } 48533 48534 MA_API float ma_delay_get_decay(const ma_delay* pDelay) 48535 { 48536 if (pDelay == NULL) { 48537 return 0; 48538 } 48539 48540 return pDelay->config.decay; 48541 } 48542 48543 48544 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) 48545 { 48546 ma_gainer_config config; 48547 48548 MA_ZERO_OBJECT(&config); 48549 config.channels = channels; 48550 config.smoothTimeInFrames = smoothTimeInFrames; 48551 48552 return config; 48553 } 48554 48555 48556 typedef struct 48557 { 48558 size_t sizeInBytes; 48559 size_t oldGainsOffset; 48560 size_t newGainsOffset; 48561 } ma_gainer_heap_layout; 48562 48563 static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) 48564 { 48565 MA_ASSERT(pHeapLayout != NULL); 48566 48567 MA_ZERO_OBJECT(pHeapLayout); 48568 48569 if (pConfig == NULL) { 48570 return MA_INVALID_ARGS; 48571 } 48572 48573 if (pConfig->channels == 0) { 48574 return MA_INVALID_ARGS; 48575 } 48576 48577 pHeapLayout->sizeInBytes = 0; 48578 48579 /* Old gains. */ 48580 pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; 48581 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; 48582 48583 /* New gains. */ 48584 pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; 48585 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; 48586 48587 /* Alignment. */ 48588 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 48589 48590 return MA_SUCCESS; 48591 } 48592 48593 48594 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) 48595 { 48596 ma_result result; 48597 ma_gainer_heap_layout heapLayout; 48598 48599 if (pHeapSizeInBytes == NULL) { 48600 return MA_INVALID_ARGS; 48601 } 48602 48603 *pHeapSizeInBytes = 0; 48604 48605 result = ma_gainer_get_heap_layout(pConfig, &heapLayout); 48606 if (result != MA_SUCCESS) { 48607 return MA_INVALID_ARGS; 48608 } 48609 48610 *pHeapSizeInBytes = heapLayout.sizeInBytes; 48611 48612 return MA_SUCCESS; 48613 } 48614 48615 48616 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) 48617 { 48618 ma_result result; 48619 ma_gainer_heap_layout heapLayout; 48620 ma_uint32 iChannel; 48621 48622 if (pGainer == NULL) { 48623 return MA_INVALID_ARGS; 48624 } 48625 48626 MA_ZERO_OBJECT(pGainer); 48627 48628 if (pConfig == NULL || pHeap == NULL) { 48629 return MA_INVALID_ARGS; 48630 } 48631 48632 result = ma_gainer_get_heap_layout(pConfig, &heapLayout); 48633 if (result != MA_SUCCESS) { 48634 return result; 48635 } 48636 48637 pGainer->_pHeap = pHeap; 48638 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 48639 48640 pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); 48641 pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); 48642 pGainer->masterVolume = 1; 48643 48644 pGainer->config = *pConfig; 48645 pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ 48646 48647 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { 48648 pGainer->pOldGains[iChannel] = 1; 48649 pGainer->pNewGains[iChannel] = 1; 48650 } 48651 48652 return MA_SUCCESS; 48653 } 48654 48655 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) 48656 { 48657 ma_result result; 48658 size_t heapSizeInBytes; 48659 void* pHeap; 48660 48661 result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); 48662 if (result != MA_SUCCESS) { 48663 return result; /* Failed to retrieve the size of the heap allocation. */ 48664 } 48665 48666 if (heapSizeInBytes > 0) { 48667 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 48668 if (pHeap == NULL) { 48669 return MA_OUT_OF_MEMORY; 48670 } 48671 } else { 48672 pHeap = NULL; 48673 } 48674 48675 result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); 48676 if (result != MA_SUCCESS) { 48677 ma_free(pHeap, pAllocationCallbacks); 48678 return result; 48679 } 48680 48681 pGainer->_ownsHeap = MA_TRUE; 48682 return MA_SUCCESS; 48683 } 48684 48685 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) 48686 { 48687 if (pGainer == NULL) { 48688 return; 48689 } 48690 48691 if (pGainer->_ownsHeap) { 48692 ma_free(pGainer->_pHeap, pAllocationCallbacks); 48693 } 48694 } 48695 48696 static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) 48697 { 48698 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; 48699 return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); 48700 } 48701 48702 static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) 48703 { 48704 ma_uint64 iFrame; 48705 ma_uint32 iChannel; 48706 ma_uint64 interpolatedFrameCount; 48707 48708 MA_ASSERT(pGainer != NULL); 48709 48710 /* 48711 We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When 48712 linear interpolation is not needed we can do a simple volume adjustment which will be more 48713 efficient than a lerp with an alpha value of 1. 48714 48715 To do this, all we need to do is determine how many frames need to have a lerp applied. Then we 48716 just process that number of frames with linear interpolation. After that we run on an optimized 48717 path which just applies the new gains without a lerp. 48718 */ 48719 if (pGainer->t >= pGainer->config.smoothTimeInFrames) { 48720 interpolatedFrameCount = 0; 48721 } else { 48722 interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; 48723 if (interpolatedFrameCount > frameCount) { 48724 interpolatedFrameCount = frameCount; 48725 } 48726 } 48727 48728 /* 48729 Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers 48730 so that the fast path can work naturally without consideration of the interpolated path. 48731 */ 48732 if (interpolatedFrameCount > 0) { 48733 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ 48734 if (pFramesOut != NULL && pFramesIn != NULL) { 48735 /* 48736 All we're really doing here is moving the old gains towards the new gains. We don't want to 48737 be modifying the gains inside the ma_gainer object because that will break things. Instead 48738 we can make a copy here on the stack. For extreme channel counts we can fall back to a slower 48739 implementation which just uses a standard lerp. 48740 */ 48741 float* pFramesOutF32 = (float*)pFramesOut; 48742 const float* pFramesInF32 = (const float*)pFramesIn; 48743 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; 48744 float d = 1.0f / pGainer->config.smoothTimeInFrames; 48745 48746 if (pGainer->config.channels <= 32) { 48747 float pRunningGain[32]; 48748 float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ 48749 48750 /* Initialize the running gain. */ 48751 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48752 float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; 48753 pRunningGainDelta[iChannel] = t * d; 48754 pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); 48755 } 48756 48757 iFrame = 0; 48758 48759 /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ 48760 if (pGainer->config.channels == 2) { 48761 #if defined(MA_SUPPORT_SSE2) 48762 if (ma_has_sse2()) { 48763 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; 48764 48765 /* Expand some arrays so we can have a clean SIMD loop below. */ 48766 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); 48767 __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); 48768 48769 for (; iFrame < unrolledLoopCount; iFrame += 1) { 48770 _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); 48771 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); 48772 } 48773 48774 iFrame = unrolledLoopCount << 1; 48775 } else 48776 #endif 48777 { 48778 /* 48779 Two different scalar implementations here. Clang (and I assume GCC) will vectorize 48780 both of these, but the bottom version results in a nicer vectorization with less 48781 instructions emitted. The problem, however, is that the bottom version runs slower 48782 when compiled with MSVC. The top version will be partially vectorized by MSVC. 48783 */ 48784 #if defined(_MSC_VER) && !defined(__clang__) 48785 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; 48786 48787 /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ 48788 pRunningGainDelta[2] = pRunningGainDelta[0]; 48789 pRunningGainDelta[3] = pRunningGainDelta[1]; 48790 pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; 48791 pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; 48792 48793 for (; iFrame < unrolledLoopCount; iFrame += 1) { 48794 pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; 48795 pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; 48796 pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; 48797 pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; 48798 48799 /* Move the running gain forward towards the new gain. */ 48800 pRunningGain[0] += pRunningGainDelta[0]; 48801 pRunningGain[1] += pRunningGainDelta[1]; 48802 pRunningGain[2] += pRunningGainDelta[2]; 48803 pRunningGain[3] += pRunningGainDelta[3]; 48804 } 48805 48806 iFrame = unrolledLoopCount << 1; 48807 #else 48808 for (; iFrame < interpolatedFrameCount; iFrame += 1) { 48809 for (iChannel = 0; iChannel < 2; iChannel += 1) { 48810 pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; 48811 } 48812 48813 for (iChannel = 0; iChannel < 2; iChannel += 1) { 48814 pRunningGain[iChannel] += pRunningGainDelta[iChannel]; 48815 } 48816 } 48817 #endif 48818 } 48819 } else if (pGainer->config.channels == 6) { 48820 #if defined(MA_SUPPORT_SSE2) 48821 if (ma_has_sse2()) { 48822 /* 48823 For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames 48824 at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays 48825 so we can do clean 4x SIMD operations. 48826 */ 48827 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; 48828 48829 /* Expand some arrays so we can have a clean SIMD loop below. */ 48830 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); 48831 __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); 48832 __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); 48833 48834 __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); 48835 __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); 48836 __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); 48837 48838 for (; iFrame < unrolledLoopCount; iFrame += 1) { 48839 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); 48840 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); 48841 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); 48842 48843 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); 48844 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); 48845 runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); 48846 } 48847 48848 iFrame = unrolledLoopCount << 1; 48849 } else 48850 #endif 48851 { 48852 for (; iFrame < interpolatedFrameCount; iFrame += 1) { 48853 for (iChannel = 0; iChannel < 6; iChannel += 1) { 48854 pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; 48855 } 48856 48857 /* Move the running gain forward towards the new gain. */ 48858 for (iChannel = 0; iChannel < 6; iChannel += 1) { 48859 pRunningGain[iChannel] += pRunningGainDelta[iChannel]; 48860 } 48861 } 48862 } 48863 } else if (pGainer->config.channels == 8) { 48864 /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ 48865 #if defined(MA_SUPPORT_SSE2) 48866 if (ma_has_sse2()) { 48867 __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); 48868 __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); 48869 __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); 48870 __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); 48871 48872 for (; iFrame < interpolatedFrameCount; iFrame += 1) { 48873 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); 48874 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); 48875 48876 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); 48877 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); 48878 } 48879 } else 48880 #endif 48881 { 48882 /* This is crafted so that it auto-vectorizes when compiled with Clang. */ 48883 for (; iFrame < interpolatedFrameCount; iFrame += 1) { 48884 for (iChannel = 0; iChannel < 8; iChannel += 1) { 48885 pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; 48886 } 48887 48888 /* Move the running gain forward towards the new gain. */ 48889 for (iChannel = 0; iChannel < 8; iChannel += 1) { 48890 pRunningGain[iChannel] += pRunningGainDelta[iChannel]; 48891 } 48892 } 48893 } 48894 } 48895 48896 for (; iFrame < interpolatedFrameCount; iFrame += 1) { 48897 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48898 pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; 48899 pRunningGain[iChannel] += pRunningGainDelta[iChannel]; 48900 } 48901 } 48902 } else { 48903 /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ 48904 for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { 48905 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48906 pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; 48907 } 48908 48909 a += d; 48910 } 48911 } 48912 } 48913 48914 /* Make sure the timer is updated. */ 48915 pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); 48916 48917 /* Adjust our arguments so the next part can work normally. */ 48918 frameCount -= interpolatedFrameCount; 48919 pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); 48920 pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); 48921 } 48922 48923 /* All we need to do here is apply the new gains using an optimized path. */ 48924 if (pFramesOut != NULL && pFramesIn != NULL) { 48925 if (pGainer->config.channels <= 32) { 48926 float gains[32]; 48927 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48928 gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; 48929 } 48930 48931 ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); 48932 } else { 48933 /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ 48934 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 48935 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48936 ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; 48937 } 48938 } 48939 } 48940 } 48941 48942 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ 48943 if (pGainer->t == (ma_uint32)-1) { 48944 pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); 48945 } 48946 48947 #if 0 48948 if (pGainer->t >= pGainer->config.smoothTimeInFrames) { 48949 /* Fast path. No gain calculation required. */ 48950 ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); 48951 ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); 48952 48953 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ 48954 if (pGainer->t == (ma_uint32)-1) { 48955 pGainer->t = pGainer->config.smoothTimeInFrames; 48956 } 48957 } else { 48958 /* Slow path. Need to interpolate the gain for each channel individually. */ 48959 48960 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ 48961 if (pFramesOut != NULL && pFramesIn != NULL) { 48962 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; 48963 float d = 1.0f / pGainer->config.smoothTimeInFrames; 48964 ma_uint32 channelCount = pGainer->config.channels; 48965 48966 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 48967 for (iChannel = 0; iChannel < channelCount; iChannel += 1) { 48968 pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; 48969 } 48970 48971 pFramesOutF32 += channelCount; 48972 pFramesInF32 += channelCount; 48973 48974 a += d; 48975 if (a > 1) { 48976 a = 1; 48977 } 48978 } 48979 } 48980 48981 pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); 48982 48983 #if 0 /* Reference implementation. */ 48984 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 48985 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ 48986 if (pFramesOut != NULL && pFramesIn != NULL) { 48987 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 48988 pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; 48989 } 48990 } 48991 48992 /* Move interpolation time forward, but don't go beyond our smoothing time. */ 48993 pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); 48994 } 48995 #endif 48996 } 48997 #endif 48998 48999 return MA_SUCCESS; 49000 } 49001 49002 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 49003 { 49004 if (pGainer == NULL) { 49005 return MA_INVALID_ARGS; 49006 } 49007 49008 /* 49009 ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which 49010 helps with auto-vectorization. 49011 */ 49012 return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); 49013 } 49014 49015 static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) 49016 { 49017 pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); 49018 pGainer->pNewGains[iChannel] = newGain; 49019 } 49020 49021 static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) 49022 { 49023 if (pGainer->t == (ma_uint32)-1) { 49024 pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ 49025 } else { 49026 pGainer->t = 0; 49027 } 49028 } 49029 49030 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) 49031 { 49032 ma_uint32 iChannel; 49033 49034 if (pGainer == NULL) { 49035 return MA_INVALID_ARGS; 49036 } 49037 49038 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 49039 ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); 49040 } 49041 49042 /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ 49043 ma_gainer_reset_smoothing_time(pGainer); 49044 49045 return MA_SUCCESS; 49046 } 49047 49048 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) 49049 { 49050 ma_uint32 iChannel; 49051 49052 if (pGainer == NULL || pNewGains == NULL) { 49053 return MA_INVALID_ARGS; 49054 } 49055 49056 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { 49057 ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); 49058 } 49059 49060 /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ 49061 ma_gainer_reset_smoothing_time(pGainer); 49062 49063 return MA_SUCCESS; 49064 } 49065 49066 MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) 49067 { 49068 if (pGainer == NULL) { 49069 return MA_INVALID_ARGS; 49070 } 49071 49072 pGainer->masterVolume = volume; 49073 49074 return MA_SUCCESS; 49075 } 49076 49077 MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) 49078 { 49079 if (pGainer == NULL || pVolume == NULL) { 49080 return MA_INVALID_ARGS; 49081 } 49082 49083 *pVolume = pGainer->masterVolume; 49084 49085 return MA_SUCCESS; 49086 } 49087 49088 49089 MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) 49090 { 49091 ma_panner_config config; 49092 49093 MA_ZERO_OBJECT(&config); 49094 config.format = format; 49095 config.channels = channels; 49096 config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ 49097 config.pan = 0; 49098 49099 return config; 49100 } 49101 49102 49103 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) 49104 { 49105 if (pPanner == NULL) { 49106 return MA_INVALID_ARGS; 49107 } 49108 49109 MA_ZERO_OBJECT(pPanner); 49110 49111 if (pConfig == NULL) { 49112 return MA_INVALID_ARGS; 49113 } 49114 49115 pPanner->format = pConfig->format; 49116 pPanner->channels = pConfig->channels; 49117 pPanner->mode = pConfig->mode; 49118 pPanner->pan = pConfig->pan; 49119 49120 return MA_SUCCESS; 49121 } 49122 49123 static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) 49124 { 49125 ma_uint64 iFrame; 49126 49127 if (pan > 0) { 49128 float factor = 1.0f - pan; 49129 if (pFramesOut == pFramesIn) { 49130 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49131 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; 49132 } 49133 } else { 49134 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49135 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; 49136 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; 49137 } 49138 } 49139 } else { 49140 float factor = 1.0f + pan; 49141 if (pFramesOut == pFramesIn) { 49142 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49143 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; 49144 } 49145 } else { 49146 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49147 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; 49148 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; 49149 } 49150 } 49151 } 49152 } 49153 49154 static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) 49155 { 49156 if (pan == 0) { 49157 /* Fast path. No panning required. */ 49158 if (pFramesOut == pFramesIn) { 49159 /* No-op */ 49160 } else { 49161 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); 49162 } 49163 49164 return; 49165 } 49166 49167 switch (format) { 49168 case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; 49169 49170 /* Unknown format. Just copy. */ 49171 default: 49172 { 49173 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); 49174 } break; 49175 } 49176 } 49177 49178 49179 static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) 49180 { 49181 ma_uint64 iFrame; 49182 49183 if (pan > 0) { 49184 float factorL0 = 1.0f - pan; 49185 float factorL1 = 0.0f + pan; 49186 49187 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49188 float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); 49189 float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; 49190 49191 pFramesOut[iFrame*2 + 0] = sample0; 49192 pFramesOut[iFrame*2 + 1] = sample1; 49193 } 49194 } else { 49195 float factorR0 = 0.0f - pan; 49196 float factorR1 = 1.0f + pan; 49197 49198 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49199 float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); 49200 float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); 49201 49202 pFramesOut[iFrame*2 + 0] = sample0; 49203 pFramesOut[iFrame*2 + 1] = sample1; 49204 } 49205 } 49206 } 49207 49208 static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) 49209 { 49210 if (pan == 0) { 49211 /* Fast path. No panning required. */ 49212 if (pFramesOut == pFramesIn) { 49213 /* No-op */ 49214 } else { 49215 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); 49216 } 49217 49218 return; 49219 } 49220 49221 switch (format) { 49222 case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; 49223 49224 /* Unknown format. Just copy. */ 49225 default: 49226 { 49227 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); 49228 } break; 49229 } 49230 } 49231 49232 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 49233 { 49234 if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { 49235 return MA_INVALID_ARGS; 49236 } 49237 49238 if (pPanner->channels == 2) { 49239 /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ 49240 if (pPanner->mode == ma_pan_mode_balance) { 49241 ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); 49242 } else { 49243 ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); 49244 } 49245 } else { 49246 if (pPanner->channels == 1) { 49247 /* Panning has no effect on mono streams. */ 49248 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); 49249 } else { 49250 /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ 49251 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); 49252 } 49253 } 49254 49255 return MA_SUCCESS; 49256 } 49257 49258 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) 49259 { 49260 if (pPanner == NULL) { 49261 return; 49262 } 49263 49264 pPanner->mode = mode; 49265 } 49266 49267 MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) 49268 { 49269 if (pPanner == NULL) { 49270 return ma_pan_mode_balance; 49271 } 49272 49273 return pPanner->mode; 49274 } 49275 49276 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) 49277 { 49278 if (pPanner == NULL) { 49279 return; 49280 } 49281 49282 pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); 49283 } 49284 49285 MA_API float ma_panner_get_pan(const ma_panner* pPanner) 49286 { 49287 if (pPanner == NULL) { 49288 return 0; 49289 } 49290 49291 return pPanner->pan; 49292 } 49293 49294 49295 49296 49297 MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 49298 { 49299 ma_fader_config config; 49300 49301 MA_ZERO_OBJECT(&config); 49302 config.format = format; 49303 config.channels = channels; 49304 config.sampleRate = sampleRate; 49305 49306 return config; 49307 } 49308 49309 49310 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) 49311 { 49312 if (pFader == NULL) { 49313 return MA_INVALID_ARGS; 49314 } 49315 49316 MA_ZERO_OBJECT(pFader); 49317 49318 if (pConfig == NULL) { 49319 return MA_INVALID_ARGS; 49320 } 49321 49322 /* Only f32 is supported for now. */ 49323 if (pConfig->format != ma_format_f32) { 49324 return MA_INVALID_ARGS; 49325 } 49326 49327 pFader->config = *pConfig; 49328 pFader->volumeBeg = 1; 49329 pFader->volumeEnd = 1; 49330 pFader->lengthInFrames = 0; 49331 pFader->cursorInFrames = 0; 49332 49333 return MA_SUCCESS; 49334 } 49335 49336 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 49337 { 49338 if (pFader == NULL) { 49339 return MA_INVALID_ARGS; 49340 } 49341 49342 /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ 49343 if (pFader->cursorInFrames < 0) { 49344 ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; 49345 if (absCursorInFrames > frameCount) { 49346 absCursorInFrames = frameCount; 49347 } 49348 49349 ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); 49350 49351 pFader->cursorInFrames += absCursorInFrames; 49352 frameCount -= absCursorInFrames; 49353 pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); 49354 pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); 49355 } 49356 49357 if (pFader->cursorInFrames >= 0) { 49358 /* 49359 For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for 49360 the conversion to a float which we use for the linear interpolation. This might be changed later. 49361 */ 49362 if (frameCount + pFader->cursorInFrames > UINT_MAX) { 49363 frameCount = UINT_MAX - pFader->cursorInFrames; 49364 } 49365 49366 /* Optimized path if volumeBeg and volumeEnd are equal. */ 49367 if (pFader->volumeBeg == pFader->volumeEnd) { 49368 if (pFader->volumeBeg == 1) { 49369 /* Straight copy. */ 49370 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); 49371 } else { 49372 /* Copy with volume. */ 49373 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); 49374 } 49375 } else { 49376 /* Slower path. Volumes are different, so may need to do an interpolation. */ 49377 if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { 49378 /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ 49379 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); 49380 } else { 49381 /* Slow path. This is where we do the actual fading. */ 49382 ma_uint64 iFrame; 49383 ma_uint32 iChannel; 49384 49385 /* For now we only support f32. Support for other formats might be added later. */ 49386 if (pFader->config.format == ma_format_f32) { 49387 const float* pFramesInF32 = (const float*)pFramesIn; 49388 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 49389 49390 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 49391 float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ 49392 float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); 49393 49394 for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { 49395 pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; 49396 } 49397 } 49398 } else { 49399 return MA_NOT_IMPLEMENTED; 49400 } 49401 } 49402 } 49403 } 49404 49405 pFader->cursorInFrames += frameCount; 49406 49407 return MA_SUCCESS; 49408 } 49409 49410 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) 49411 { 49412 if (pFader == NULL) { 49413 return; 49414 } 49415 49416 if (pFormat != NULL) { 49417 *pFormat = pFader->config.format; 49418 } 49419 49420 if (pChannels != NULL) { 49421 *pChannels = pFader->config.channels; 49422 } 49423 49424 if (pSampleRate != NULL) { 49425 *pSampleRate = pFader->config.sampleRate; 49426 } 49427 } 49428 49429 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) 49430 { 49431 ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); 49432 } 49433 49434 MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) 49435 { 49436 if (pFader == NULL) { 49437 return; 49438 } 49439 49440 /* If the volume is negative, use current volume. */ 49441 if (volumeBeg < 0) { 49442 volumeBeg = ma_fader_get_current_volume(pFader); 49443 } 49444 49445 /* 49446 The length needs to be clamped to 32-bits due to how we convert it to a float for linear 49447 interpolation reasons. I might change this requirement later, but for now it's not important. 49448 */ 49449 if (lengthInFrames > UINT_MAX) { 49450 lengthInFrames = UINT_MAX; 49451 } 49452 49453 /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ 49454 if (startOffsetInFrames > INT_MAX) { 49455 startOffsetInFrames = INT_MAX; 49456 } 49457 49458 pFader->volumeBeg = volumeBeg; 49459 pFader->volumeEnd = volumeEnd; 49460 pFader->lengthInFrames = lengthInFrames; 49461 pFader->cursorInFrames = -startOffsetInFrames; 49462 } 49463 49464 MA_API float ma_fader_get_current_volume(const ma_fader* pFader) 49465 { 49466 if (pFader == NULL) { 49467 return 0.0f; 49468 } 49469 49470 /* Any frames prior to the start of the fade period will be at unfaded volume. */ 49471 if (pFader->cursorInFrames < 0) { 49472 return 1.0f; 49473 } 49474 49475 /* The current volume depends on the position of the cursor. */ 49476 if (pFader->cursorInFrames == 0) { 49477 return pFader->volumeBeg; 49478 } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ 49479 return pFader->volumeEnd; 49480 } else { 49481 /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ 49482 return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ 49483 } 49484 } 49485 49486 49487 49488 49489 49490 MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) 49491 { 49492 ma_vec3f v; 49493 49494 v.x = x; 49495 v.y = y; 49496 v.z = z; 49497 49498 return v; 49499 } 49500 49501 MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) 49502 { 49503 return ma_vec3f_init_3f( 49504 a.x - b.x, 49505 a.y - b.y, 49506 a.z - b.z 49507 ); 49508 } 49509 49510 MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) 49511 { 49512 return ma_vec3f_init_3f( 49513 -a.x, 49514 -a.y, 49515 -a.z 49516 ); 49517 } 49518 49519 MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) 49520 { 49521 return a.x*b.x + a.y*b.y + a.z*b.z; 49522 } 49523 49524 MA_API float ma_vec3f_len2(ma_vec3f v) 49525 { 49526 return ma_vec3f_dot(v, v); 49527 } 49528 49529 MA_API float ma_vec3f_len(ma_vec3f v) 49530 { 49531 return (float)ma_sqrtd(ma_vec3f_len2(v)); 49532 } 49533 49534 49535 49536 MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) 49537 { 49538 return ma_vec3f_len(ma_vec3f_sub(a, b)); 49539 } 49540 49541 MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) 49542 { 49543 float invLen; 49544 float len2 = ma_vec3f_len2(v); 49545 if (len2 == 0) { 49546 return ma_vec3f_init_3f(0, 0, 0); 49547 } 49548 49549 invLen = ma_rsqrtf(len2); 49550 v.x *= invLen; 49551 v.y *= invLen; 49552 v.z *= invLen; 49553 49554 return v; 49555 } 49556 49557 MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) 49558 { 49559 return ma_vec3f_init_3f( 49560 a.y*b.z - a.z*b.y, 49561 a.z*b.x - a.x*b.z, 49562 a.x*b.y - a.y*b.x 49563 ); 49564 } 49565 49566 49567 MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) 49568 { 49569 v->v = value; 49570 v->lock = 0; /* Important this is initialized to 0. */ 49571 } 49572 49573 MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) 49574 { 49575 ma_spinlock_lock(&v->lock); 49576 { 49577 v->v = value; 49578 } 49579 ma_spinlock_unlock(&v->lock); 49580 } 49581 49582 MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) 49583 { 49584 ma_vec3f r; 49585 49586 ma_spinlock_lock(&v->lock); 49587 { 49588 r = v->v; 49589 } 49590 ma_spinlock_unlock(&v->lock); 49591 49592 return r; 49593 } 49594 49595 49596 49597 static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); 49598 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); 49599 49600 49601 #ifndef MA_DEFAULT_SPEED_OF_SOUND 49602 #define MA_DEFAULT_SPEED_OF_SOUND 343.3f 49603 #endif 49604 49605 /* 49606 These vectors represent the direction that speakers are facing from the center point. They're used 49607 for panning in the spatializer. Must be normalized. 49608 */ 49609 static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { 49610 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ 49611 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ 49612 {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ 49613 {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ 49614 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ 49615 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ 49616 {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ 49617 {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ 49618 {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ 49619 {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ 49620 { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ 49621 {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ 49622 {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ 49623 { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ 49624 {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ 49625 { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ 49626 {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ 49627 {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ 49628 { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ 49629 {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ 49630 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ 49631 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ 49632 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ 49633 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ 49634 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ 49635 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ 49636 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ 49637 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ 49638 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ 49639 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ 49640 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ 49641 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ 49642 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ 49643 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ 49644 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ 49645 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ 49646 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ 49647 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ 49648 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ 49649 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ 49650 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ 49651 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ 49652 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ 49653 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ 49654 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ 49655 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ 49656 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ 49657 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ 49658 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ 49659 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ 49660 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ 49661 { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ 49662 }; 49663 49664 static ma_vec3f ma_get_channel_direction(ma_channel channel) 49665 { 49666 if (channel >= MA_CHANNEL_POSITION_COUNT) { 49667 return ma_vec3f_init_3f(0, 0, -1); 49668 } else { 49669 return g_maChannelDirections[channel]; 49670 } 49671 } 49672 49673 49674 49675 static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) 49676 { 49677 if (minDistance >= maxDistance) { 49678 return 1; /* To avoid division by zero. Do not attenuate. */ 49679 } 49680 49681 return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); 49682 } 49683 49684 static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) 49685 { 49686 if (minDistance >= maxDistance) { 49687 return 1; /* To avoid division by zero. Do not attenuate. */ 49688 } 49689 49690 return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); 49691 } 49692 49693 static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) 49694 { 49695 if (minDistance >= maxDistance) { 49696 return 1; /* To avoid division by zero. Do not attenuate. */ 49697 } 49698 49699 return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); 49700 } 49701 49702 49703 /* 49704 Dopper Effect calculation taken from the OpenAL spec, with two main differences: 49705 49706 1) The source to listener vector will have already been calcualted at an earlier step so we can 49707 just use that directly. We need only the position of the source relative to the origin. 49708 49709 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight 49710 into the resampler directly. 49711 */ 49712 static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) 49713 { 49714 float len; 49715 float vls; 49716 float vss; 49717 49718 len = ma_vec3f_len(relativePosition); 49719 49720 /* 49721 There's a case where the position of the source will be right on top of the listener in which 49722 case the length will be 0 and we'll end up with a division by zero. We can just return a ratio 49723 of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. 49724 */ 49725 if (len == 0) { 49726 return 1.0; 49727 } 49728 49729 vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; 49730 vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; 49731 49732 vls = ma_min(vls, speedOfSound / dopplerFactor); 49733 vss = ma_min(vss, speedOfSound / dopplerFactor); 49734 49735 return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); 49736 } 49737 49738 49739 static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) 49740 { 49741 /* 49742 Special case for stereo. Want to default the left and right speakers to side left and side 49743 right so that they're facing directly down the X axis rather than slightly forward. Not 49744 doing this will result in sounds being quieter when behind the listener. This might 49745 actually be good for some scenerios, but I don't think it's an appropriate default because 49746 it can be a bit unexpected. 49747 */ 49748 if (channelCount == 2) { 49749 pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; 49750 pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; 49751 } else { 49752 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); 49753 } 49754 } 49755 49756 49757 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) 49758 { 49759 ma_spatializer_listener_config config; 49760 49761 MA_ZERO_OBJECT(&config); 49762 config.channelsOut = channelsOut; 49763 config.pChannelMapOut = NULL; 49764 config.handedness = ma_handedness_right; 49765 config.worldUp = ma_vec3f_init_3f(0, 1, 0); 49766 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ 49767 config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ 49768 config.coneOuterGain = 0; 49769 config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ 49770 49771 return config; 49772 } 49773 49774 49775 typedef struct 49776 { 49777 size_t sizeInBytes; 49778 size_t channelMapOutOffset; 49779 } ma_spatializer_listener_heap_layout; 49780 49781 static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) 49782 { 49783 MA_ASSERT(pHeapLayout != NULL); 49784 49785 MA_ZERO_OBJECT(pHeapLayout); 49786 49787 if (pConfig == NULL) { 49788 return MA_INVALID_ARGS; 49789 } 49790 49791 if (pConfig->channelsOut == 0) { 49792 return MA_INVALID_ARGS; 49793 } 49794 49795 pHeapLayout->sizeInBytes = 0; 49796 49797 /* Channel map. We always need this, even for passthroughs. */ 49798 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; 49799 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); 49800 49801 return MA_SUCCESS; 49802 } 49803 49804 49805 MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) 49806 { 49807 ma_result result; 49808 ma_spatializer_listener_heap_layout heapLayout; 49809 49810 if (pHeapSizeInBytes == NULL) { 49811 return MA_INVALID_ARGS; 49812 } 49813 49814 *pHeapSizeInBytes = 0; 49815 49816 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); 49817 if (result != MA_SUCCESS) { 49818 return result; 49819 } 49820 49821 *pHeapSizeInBytes = heapLayout.sizeInBytes; 49822 49823 return MA_SUCCESS; 49824 } 49825 49826 MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) 49827 { 49828 ma_result result; 49829 ma_spatializer_listener_heap_layout heapLayout; 49830 49831 if (pListener == NULL) { 49832 return MA_INVALID_ARGS; 49833 } 49834 49835 MA_ZERO_OBJECT(pListener); 49836 49837 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); 49838 if (result != MA_SUCCESS) { 49839 return result; 49840 } 49841 49842 pListener->_pHeap = pHeap; 49843 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 49844 49845 pListener->config = *pConfig; 49846 ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); 49847 ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); 49848 ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); 49849 pListener->isEnabled = MA_TRUE; 49850 49851 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ 49852 if (pListener->config.handedness == ma_handedness_left) { 49853 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); 49854 ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); 49855 } 49856 49857 49858 /* We must always have a valid channel map. */ 49859 pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); 49860 49861 /* Use a slightly different default channel map for stereo. */ 49862 if (pConfig->pChannelMapOut == NULL) { 49863 ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); 49864 } else { 49865 ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); 49866 } 49867 49868 return MA_SUCCESS; 49869 } 49870 49871 MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) 49872 { 49873 ma_result result; 49874 size_t heapSizeInBytes; 49875 void* pHeap; 49876 49877 result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); 49878 if (result != MA_SUCCESS) { 49879 return result; 49880 } 49881 49882 if (heapSizeInBytes > 0) { 49883 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 49884 if (pHeap == NULL) { 49885 return MA_OUT_OF_MEMORY; 49886 } 49887 } else { 49888 pHeap = NULL; 49889 } 49890 49891 result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); 49892 if (result != MA_SUCCESS) { 49893 ma_free(pHeap, pAllocationCallbacks); 49894 return result; 49895 } 49896 49897 pListener->_ownsHeap = MA_TRUE; 49898 return MA_SUCCESS; 49899 } 49900 49901 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) 49902 { 49903 if (pListener == NULL) { 49904 return; 49905 } 49906 49907 if (pListener->_ownsHeap) { 49908 ma_free(pListener->_pHeap, pAllocationCallbacks); 49909 } 49910 } 49911 49912 MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) 49913 { 49914 if (pListener == NULL) { 49915 return NULL; 49916 } 49917 49918 return pListener->config.pChannelMapOut; 49919 } 49920 49921 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) 49922 { 49923 if (pListener == NULL) { 49924 return; 49925 } 49926 49927 pListener->config.coneInnerAngleInRadians = innerAngleInRadians; 49928 pListener->config.coneOuterAngleInRadians = outerAngleInRadians; 49929 pListener->config.coneOuterGain = outerGain; 49930 } 49931 49932 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) 49933 { 49934 if (pListener == NULL) { 49935 return; 49936 } 49937 49938 if (pInnerAngleInRadians != NULL) { 49939 *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; 49940 } 49941 49942 if (pOuterAngleInRadians != NULL) { 49943 *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; 49944 } 49945 49946 if (pOuterGain != NULL) { 49947 *pOuterGain = pListener->config.coneOuterGain; 49948 } 49949 } 49950 49951 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) 49952 { 49953 if (pListener == NULL) { 49954 return; 49955 } 49956 49957 ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); 49958 } 49959 49960 MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) 49961 { 49962 if (pListener == NULL) { 49963 return ma_vec3f_init_3f(0, 0, 0); 49964 } 49965 49966 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 49967 } 49968 49969 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) 49970 { 49971 if (pListener == NULL) { 49972 return; 49973 } 49974 49975 ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); 49976 } 49977 49978 MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) 49979 { 49980 if (pListener == NULL) { 49981 return ma_vec3f_init_3f(0, 0, -1); 49982 } 49983 49984 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 49985 } 49986 49987 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) 49988 { 49989 if (pListener == NULL) { 49990 return; 49991 } 49992 49993 ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); 49994 } 49995 49996 MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) 49997 { 49998 if (pListener == NULL) { 49999 return ma_vec3f_init_3f(0, 0, 0); 50000 } 50001 50002 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 50003 } 50004 50005 MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) 50006 { 50007 if (pListener == NULL) { 50008 return; 50009 } 50010 50011 pListener->config.speedOfSound = speedOfSound; 50012 } 50013 50014 MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) 50015 { 50016 if (pListener == NULL) { 50017 return 0; 50018 } 50019 50020 return pListener->config.speedOfSound; 50021 } 50022 50023 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) 50024 { 50025 if (pListener == NULL) { 50026 return; 50027 } 50028 50029 pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); 50030 } 50031 50032 MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) 50033 { 50034 if (pListener == NULL) { 50035 return ma_vec3f_init_3f(0, 1, 0); 50036 } 50037 50038 return pListener->config.worldUp; 50039 } 50040 50041 MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) 50042 { 50043 if (pListener == NULL) { 50044 return; 50045 } 50046 50047 pListener->isEnabled = isEnabled; 50048 } 50049 50050 MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) 50051 { 50052 if (pListener == NULL) { 50053 return MA_FALSE; 50054 } 50055 50056 return pListener->isEnabled; 50057 } 50058 50059 50060 50061 50062 MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) 50063 { 50064 ma_spatializer_config config; 50065 50066 MA_ZERO_OBJECT(&config); 50067 config.channelsIn = channelsIn; 50068 config.channelsOut = channelsOut; 50069 config.pChannelMapIn = NULL; 50070 config.attenuationModel = ma_attenuation_model_inverse; 50071 config.positioning = ma_positioning_absolute; 50072 config.handedness = ma_handedness_right; 50073 config.minGain = 0; 50074 config.maxGain = 1; 50075 config.minDistance = 1; 50076 config.maxDistance = MA_FLT_MAX; 50077 config.rolloff = 1; 50078 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ 50079 config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ 50080 config.coneOuterGain = 0.0f; 50081 config.dopplerFactor = 1; 50082 config.directionalAttenuationFactor = 1; 50083 config.minSpatializationChannelGain = 0.2f; 50084 config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ 50085 50086 return config; 50087 } 50088 50089 50090 static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) 50091 { 50092 MA_ASSERT(pConfig != NULL); 50093 return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); 50094 } 50095 50096 static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) 50097 { 50098 MA_ASSERT(pConfig != NULL); 50099 50100 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { 50101 return MA_INVALID_ARGS; 50102 } 50103 50104 return MA_SUCCESS; 50105 } 50106 50107 typedef struct 50108 { 50109 size_t sizeInBytes; 50110 size_t channelMapInOffset; 50111 size_t newChannelGainsOffset; 50112 size_t gainerOffset; 50113 } ma_spatializer_heap_layout; 50114 50115 static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) 50116 { 50117 ma_result result; 50118 50119 MA_ASSERT(pHeapLayout != NULL); 50120 50121 MA_ZERO_OBJECT(pHeapLayout); 50122 50123 if (pConfig == NULL) { 50124 return MA_INVALID_ARGS; 50125 } 50126 50127 result = ma_spatializer_validate_config(pConfig); 50128 if (result != MA_SUCCESS) { 50129 return result; 50130 } 50131 50132 pHeapLayout->sizeInBytes = 0; 50133 50134 /* Channel map. */ 50135 pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ 50136 if (pConfig->pChannelMapIn != NULL) { 50137 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; 50138 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); 50139 } 50140 50141 /* New channel gains for output. */ 50142 pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; 50143 pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); 50144 50145 /* Gainer. */ 50146 { 50147 size_t gainerHeapSizeInBytes; 50148 ma_gainer_config gainerConfig; 50149 50150 gainerConfig = ma_spatializer_gainer_config_init(pConfig); 50151 50152 result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); 50153 if (result != MA_SUCCESS) { 50154 return result; 50155 } 50156 50157 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; 50158 pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); 50159 } 50160 50161 return MA_SUCCESS; 50162 } 50163 50164 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) 50165 { 50166 ma_result result; 50167 ma_spatializer_heap_layout heapLayout; 50168 50169 if (pHeapSizeInBytes == NULL) { 50170 return MA_INVALID_ARGS; 50171 } 50172 50173 *pHeapSizeInBytes = 0; /* Safety. */ 50174 50175 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); 50176 if (result != MA_SUCCESS) { 50177 return result; 50178 } 50179 50180 *pHeapSizeInBytes = heapLayout.sizeInBytes; 50181 50182 return MA_SUCCESS; 50183 } 50184 50185 50186 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) 50187 { 50188 ma_result result; 50189 ma_spatializer_heap_layout heapLayout; 50190 ma_gainer_config gainerConfig; 50191 50192 if (pSpatializer == NULL) { 50193 return MA_INVALID_ARGS; 50194 } 50195 50196 MA_ZERO_OBJECT(pSpatializer); 50197 50198 if (pConfig == NULL || pHeap == NULL) { 50199 return MA_INVALID_ARGS; 50200 } 50201 50202 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); 50203 if (result != MA_SUCCESS) { 50204 return result; 50205 } 50206 50207 pSpatializer->_pHeap = pHeap; 50208 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 50209 50210 pSpatializer->channelsIn = pConfig->channelsIn; 50211 pSpatializer->channelsOut = pConfig->channelsOut; 50212 pSpatializer->attenuationModel = pConfig->attenuationModel; 50213 pSpatializer->positioning = pConfig->positioning; 50214 pSpatializer->handedness = pConfig->handedness; 50215 pSpatializer->minGain = pConfig->minGain; 50216 pSpatializer->maxGain = pConfig->maxGain; 50217 pSpatializer->minDistance = pConfig->minDistance; 50218 pSpatializer->maxDistance = pConfig->maxDistance; 50219 pSpatializer->rolloff = pConfig->rolloff; 50220 pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; 50221 pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; 50222 pSpatializer->coneOuterGain = pConfig->coneOuterGain; 50223 pSpatializer->dopplerFactor = pConfig->dopplerFactor; 50224 pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; 50225 pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; 50226 pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; 50227 ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); 50228 ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); 50229 ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); 50230 pSpatializer->dopplerPitch = 1; 50231 50232 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ 50233 if (pSpatializer->handedness == ma_handedness_left) { 50234 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); 50235 ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); 50236 } 50237 50238 /* Channel map. This will be on the heap. */ 50239 if (pConfig->pChannelMapIn != NULL) { 50240 pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); 50241 ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); 50242 } 50243 50244 /* New channel gains for output channels. */ 50245 pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); 50246 50247 /* Gainer. */ 50248 gainerConfig = ma_spatializer_gainer_config_init(pConfig); 50249 50250 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); 50251 if (result != MA_SUCCESS) { 50252 return result; /* Failed to initialize the gainer. */ 50253 } 50254 50255 return MA_SUCCESS; 50256 } 50257 50258 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) 50259 { 50260 ma_result result; 50261 size_t heapSizeInBytes; 50262 void* pHeap; 50263 50264 /* We'll need a heap allocation to retrieve the size. */ 50265 result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); 50266 if (result != MA_SUCCESS) { 50267 return result; 50268 } 50269 50270 if (heapSizeInBytes > 0) { 50271 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 50272 if (pHeap == NULL) { 50273 return MA_OUT_OF_MEMORY; 50274 } 50275 } else { 50276 pHeap = NULL; 50277 } 50278 50279 result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); 50280 if (result != MA_SUCCESS) { 50281 ma_free(pHeap, pAllocationCallbacks); 50282 return result; 50283 } 50284 50285 pSpatializer->_ownsHeap = MA_TRUE; 50286 return MA_SUCCESS; 50287 } 50288 50289 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) 50290 { 50291 if (pSpatializer == NULL) { 50292 return; 50293 } 50294 50295 ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); 50296 50297 if (pSpatializer->_ownsHeap) { 50298 ma_free(pSpatializer->_pHeap, pAllocationCallbacks); 50299 } 50300 } 50301 50302 static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) 50303 { 50304 /* 50305 Angular attenuation. 50306 50307 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure 50308 this out for ourselves at the expense of possibly being inconsistent with other implementations. 50309 50310 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We 50311 just need to get the direction from the source to the listener and then do a dot product against that and the 50312 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer 50313 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than 50314 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. 50315 */ 50316 if (coneInnerAngleInRadians < 6.283185f) { 50317 float angularGain = 1; 50318 float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); 50319 float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); 50320 float d; 50321 50322 d = ma_vec3f_dot(dirA, dirB); 50323 50324 if (d > cutoffInner) { 50325 /* It's inside the inner angle. */ 50326 angularGain = 1; 50327 } else { 50328 /* It's outside the inner angle. */ 50329 if (d > cutoffOuter) { 50330 /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ 50331 angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); 50332 } else { 50333 /* It's outside the outer angle. */ 50334 angularGain = coneOuterGain; 50335 } 50336 } 50337 50338 /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ 50339 return angularGain; 50340 } else { 50341 /* Inner angle is 360 degrees so no need to do any attenuation. */ 50342 return 1; 50343 } 50344 } 50345 50346 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 50347 { 50348 ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; 50349 ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; 50350 50351 if (pSpatializer == NULL) { 50352 return MA_INVALID_ARGS; 50353 } 50354 50355 /* If we're not spatializing we need to run an optimized path. */ 50356 if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { 50357 if (ma_spatializer_listener_is_enabled(pListener)) { 50358 /* No attenuation is required, but we'll need to do some channel conversion. */ 50359 if (pSpatializer->channelsIn == pSpatializer->channelsOut) { 50360 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); 50361 } else { 50362 ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ 50363 } 50364 } else { 50365 /* The listener is disabled. Output silence. */ 50366 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); 50367 } 50368 50369 /* 50370 We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is 50371 the correct thinking so might need to review this later. 50372 */ 50373 pSpatializer->dopplerPitch = 1; 50374 } else { 50375 /* 50376 Let's first determine which listener the sound is closest to. Need to keep in mind that we 50377 might not have a world or any listeners, in which case we just spatializer based on the 50378 listener being positioned at the origin (0, 0, 0). 50379 */ 50380 ma_vec3f relativePosNormalized; 50381 ma_vec3f relativePos; /* The position relative to the listener. */ 50382 ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ 50383 ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ 50384 float speedOfSound; 50385 float distance = 0; 50386 float gain = 1; 50387 ma_uint32 iChannel; 50388 const ma_uint32 channelsOut = pSpatializer->channelsOut; 50389 const ma_uint32 channelsIn = pSpatializer->channelsIn; 50390 float minDistance = ma_spatializer_get_min_distance(pSpatializer); 50391 float maxDistance = ma_spatializer_get_max_distance(pSpatializer); 50392 float rolloff = ma_spatializer_get_rolloff(pSpatializer); 50393 float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); 50394 50395 /* 50396 We'll need the listener velocity for doppler pitch calculations. The speed of sound is 50397 defined by the listener, so we'll grab that here too. 50398 */ 50399 if (pListener != NULL) { 50400 listenerVel = ma_spatializer_listener_get_velocity(pListener); 50401 speedOfSound = pListener->config.speedOfSound; 50402 } else { 50403 listenerVel = ma_vec3f_init_3f(0, 0, 0); 50404 speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; 50405 } 50406 50407 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { 50408 /* There's no listener or we're using relative positioning. */ 50409 relativePos = ma_spatializer_get_position(pSpatializer); 50410 relativeDir = ma_spatializer_get_direction(pSpatializer); 50411 } else { 50412 /* 50413 We've found a listener and we're using absolute positioning. We need to transform the 50414 sound's position and direction so that it's relative to listener. Later on we'll use 50415 this for determining the factors to apply to each channel to apply the panning effect. 50416 */ 50417 ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); 50418 } 50419 50420 distance = ma_vec3f_len(relativePos); 50421 50422 /* We've gathered the data, so now we can apply some spatialization. */ 50423 switch (ma_spatializer_get_attenuation_model(pSpatializer)) { 50424 case ma_attenuation_model_inverse: 50425 { 50426 gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); 50427 } break; 50428 case ma_attenuation_model_linear: 50429 { 50430 gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); 50431 } break; 50432 case ma_attenuation_model_exponential: 50433 { 50434 gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); 50435 } break; 50436 case ma_attenuation_model_none: 50437 default: 50438 { 50439 gain = 1; 50440 } break; 50441 } 50442 50443 /* Normalize the position. */ 50444 if (distance > 0.001f) { 50445 float distanceInv = 1/distance; 50446 relativePosNormalized = relativePos; 50447 relativePosNormalized.x *= distanceInv; 50448 relativePosNormalized.y *= distanceInv; 50449 relativePosNormalized.z *= distanceInv; 50450 } else { 50451 distance = 0; 50452 relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); 50453 } 50454 50455 /* 50456 Angular attenuation. 50457 50458 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure 50459 this out for ourselves at the expense of possibly being inconsistent with other implementations. 50460 50461 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We 50462 just need to get the direction from the source to the listener and then do a dot product against that and the 50463 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer 50464 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than 50465 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. 50466 */ 50467 if (distance > 0) { 50468 /* Source anglular gain. */ 50469 float spatializerConeInnerAngle; 50470 float spatializerConeOuterAngle; 50471 float spatializerConeOuterGain; 50472 ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); 50473 50474 gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); 50475 50476 /* 50477 We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that 50478 are positioned behind the listener. On default settings, this will have no effect. 50479 */ 50480 if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { 50481 ma_vec3f listenerDirection; 50482 float listenerInnerAngle; 50483 float listenerOuterAngle; 50484 float listenerOuterGain; 50485 50486 if (pListener->config.handedness == ma_handedness_right) { 50487 listenerDirection = ma_vec3f_init_3f(0, 0, -1); 50488 } else { 50489 listenerDirection = ma_vec3f_init_3f(0, 0, +1); 50490 } 50491 50492 listenerInnerAngle = pListener->config.coneInnerAngleInRadians; 50493 listenerOuterAngle = pListener->config.coneOuterAngleInRadians; 50494 listenerOuterGain = pListener->config.coneOuterGain; 50495 50496 gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); 50497 } 50498 } else { 50499 /* The sound is right on top of the listener. Don't do any angular attenuation. */ 50500 } 50501 50502 50503 /* Clamp the gain. */ 50504 gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); 50505 50506 /* 50507 The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel 50508 gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions 50509 to avoid harsh changes in gain. 50510 */ 50511 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { 50512 pSpatializer->pNewChannelGainsOut[iChannel] = gain; 50513 } 50514 50515 /* 50516 Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore 50517 the whole section of code here because we need to update some internal spatialization state. 50518 */ 50519 if (ma_spatializer_listener_is_enabled(pListener)) { 50520 ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); 50521 } else { 50522 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); 50523 } 50524 50525 50526 /* 50527 Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for 50528 when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the 50529 gain to the final output. 50530 */ 50531 /*printf("distance=%f; gain=%f\n", distance, gain);*/ 50532 50533 /* We must have a valid channel map here to ensure we spatialize properly. */ 50534 MA_ASSERT(pChannelMapOut != NULL); 50535 50536 /* 50537 We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being 50538 to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that 50539 the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and 50540 seeing how it goes. There might be better ways to do this. 50541 50542 To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a 50543 direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will 50544 be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized 50545 position of the sound. 50546 */ 50547 50548 /* 50549 Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's 50550 relation to the direction of the channel. 50551 */ 50552 if (distance > 0) { 50553 ma_vec3f unitPos = relativePos; 50554 float distanceInv = 1/distance; 50555 unitPos.x *= distanceInv; 50556 unitPos.y *= distanceInv; 50557 unitPos.z *= distanceInv; 50558 50559 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { 50560 ma_channel channelOut; 50561 float d; 50562 float dMin; 50563 50564 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); 50565 if (ma_is_spatial_channel_position(channelOut)) { 50566 d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); 50567 } else { 50568 d = 1; /* It's not a spatial channel so there's no real notion of direction. */ 50569 } 50570 50571 /* 50572 In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. 50573 The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to 50574 0, panning will be most extreme and any sounds that are positioned on the opposite side of the 50575 speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it 50576 doesn't even remotely represent the real world at all because sounds that come from your right side 50577 are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at 50578 all, which is also not ideal. By setting it to something greater than 0, the spatialization effect 50579 becomes much less dramatic and a lot more bearable. 50580 50581 Summary: 0 = more extreme panning; 1 = no panning. 50582 */ 50583 dMin = pSpatializer->minSpatializationChannelGain; 50584 50585 /* 50586 At this point, "d" will be positive if the sound is on the same side as the channel and negative if 50587 it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to 50588 calculate a panning value. The first is to simply convert it to 0..1, however this has a problem 50589 which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right 50590 in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like 50591 the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front 50592 of the listener. I would intuitively expect that to be played at full volume, or close to it. 50593 50594 The second idea I think of is to only apply a reduction in gain when the sound is on the opposite 50595 side of the speaker. That is, reduce the gain only when the dot product is negative. The problem 50596 with this is that there will not be any attenuation as the sound sweeps around the 180 degrees 50597 where the dot product is positive. The idea with this option is that you leave the gain at 1 when 50598 the sound is being played on the same side as the speaker and then you just reduce the volume when 50599 the sound is on the other side. 50600 50601 The summarize, I think the first option should give a better sense of spatialization, but the second 50602 option is better for preserving the sound's power. 50603 50604 UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a 50605 bit better, but you can also hear the reduction in volume when it's right in front. 50606 */ 50607 #if 1 50608 { 50609 /* 50610 Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power 50611 by being played at 0.5 gain. 50612 */ 50613 d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ 50614 d = ma_max(d, dMin); 50615 pSpatializer->pNewChannelGainsOut[iChannel] *= d; 50616 } 50617 #else 50618 { 50619 /* 50620 Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more 50621 consistent, but comes at the expense of a worse sense of space and positioning. 50622 */ 50623 if (d < 0) { 50624 d += 1; /* Move into the positive range. */ 50625 d = ma_max(d, dMin); 50626 channelGainsOut[iChannel] *= d; 50627 } 50628 } 50629 #endif 50630 } 50631 } else { 50632 /* Assume the sound is right on top of us. Don't do any panning. */ 50633 } 50634 50635 /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ 50636 ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); 50637 ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); 50638 50639 /* 50640 Before leaving we'll want to update our doppler pitch so that the caller can apply some 50641 pitch shifting if they desire. Note that we need to negate the relative position here 50642 because the doppler calculation needs to be source-to-listener, but ours is listener-to- 50643 source. 50644 */ 50645 if (dopplerFactor > 0) { 50646 pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); 50647 } else { 50648 pSpatializer->dopplerPitch = 1; 50649 } 50650 } 50651 50652 return MA_SUCCESS; 50653 } 50654 50655 MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) 50656 { 50657 if (pSpatializer == NULL) { 50658 return MA_INVALID_ARGS; 50659 } 50660 50661 return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); 50662 } 50663 50664 MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) 50665 { 50666 if (pSpatializer == NULL) { 50667 return MA_INVALID_ARGS; 50668 } 50669 50670 return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); 50671 } 50672 50673 MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) 50674 { 50675 if (pSpatializer == NULL) { 50676 return 0; 50677 } 50678 50679 return pSpatializer->channelsIn; 50680 } 50681 50682 MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) 50683 { 50684 if (pSpatializer == NULL) { 50685 return 0; 50686 } 50687 50688 return pSpatializer->channelsOut; 50689 } 50690 50691 MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) 50692 { 50693 if (pSpatializer == NULL) { 50694 return; 50695 } 50696 50697 ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); 50698 } 50699 50700 MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) 50701 { 50702 if (pSpatializer == NULL) { 50703 return ma_attenuation_model_none; 50704 } 50705 50706 return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); 50707 } 50708 50709 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) 50710 { 50711 if (pSpatializer == NULL) { 50712 return; 50713 } 50714 50715 ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); 50716 } 50717 50718 MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) 50719 { 50720 if (pSpatializer == NULL) { 50721 return ma_positioning_absolute; 50722 } 50723 50724 return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); 50725 } 50726 50727 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) 50728 { 50729 if (pSpatializer == NULL) { 50730 return; 50731 } 50732 50733 ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); 50734 } 50735 50736 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) 50737 { 50738 if (pSpatializer == NULL) { 50739 return 0; 50740 } 50741 50742 return ma_atomic_load_f32(&pSpatializer->rolloff); 50743 } 50744 50745 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) 50746 { 50747 if (pSpatializer == NULL) { 50748 return; 50749 } 50750 50751 ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); 50752 } 50753 50754 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) 50755 { 50756 if (pSpatializer == NULL) { 50757 return 0; 50758 } 50759 50760 return ma_atomic_load_f32(&pSpatializer->minGain); 50761 } 50762 50763 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) 50764 { 50765 if (pSpatializer == NULL) { 50766 return; 50767 } 50768 50769 ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); 50770 } 50771 50772 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) 50773 { 50774 if (pSpatializer == NULL) { 50775 return 0; 50776 } 50777 50778 return ma_atomic_load_f32(&pSpatializer->maxGain); 50779 } 50780 50781 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) 50782 { 50783 if (pSpatializer == NULL) { 50784 return; 50785 } 50786 50787 ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); 50788 } 50789 50790 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) 50791 { 50792 if (pSpatializer == NULL) { 50793 return 0; 50794 } 50795 50796 return ma_atomic_load_f32(&pSpatializer->minDistance); 50797 } 50798 50799 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) 50800 { 50801 if (pSpatializer == NULL) { 50802 return; 50803 } 50804 50805 ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); 50806 } 50807 50808 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) 50809 { 50810 if (pSpatializer == NULL) { 50811 return 0; 50812 } 50813 50814 return ma_atomic_load_f32(&pSpatializer->maxDistance); 50815 } 50816 50817 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) 50818 { 50819 if (pSpatializer == NULL) { 50820 return; 50821 } 50822 50823 ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); 50824 ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); 50825 ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); 50826 } 50827 50828 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) 50829 { 50830 if (pSpatializer == NULL) { 50831 return; 50832 } 50833 50834 if (pInnerAngleInRadians != NULL) { 50835 *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); 50836 } 50837 50838 if (pOuterAngleInRadians != NULL) { 50839 *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); 50840 } 50841 50842 if (pOuterGain != NULL) { 50843 *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); 50844 } 50845 } 50846 50847 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) 50848 { 50849 if (pSpatializer == NULL) { 50850 return; 50851 } 50852 50853 ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); 50854 } 50855 50856 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) 50857 { 50858 if (pSpatializer == NULL) { 50859 return 1; 50860 } 50861 50862 return ma_atomic_load_f32(&pSpatializer->dopplerFactor); 50863 } 50864 50865 MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) 50866 { 50867 if (pSpatializer == NULL) { 50868 return; 50869 } 50870 50871 ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); 50872 } 50873 50874 MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) 50875 { 50876 if (pSpatializer == NULL) { 50877 return 1; 50878 } 50879 50880 return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); 50881 } 50882 50883 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) 50884 { 50885 if (pSpatializer == NULL) { 50886 return; 50887 } 50888 50889 ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); 50890 } 50891 50892 MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) 50893 { 50894 if (pSpatializer == NULL) { 50895 return ma_vec3f_init_3f(0, 0, 0); 50896 } 50897 50898 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 50899 } 50900 50901 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) 50902 { 50903 if (pSpatializer == NULL) { 50904 return; 50905 } 50906 50907 ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); 50908 } 50909 50910 MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) 50911 { 50912 if (pSpatializer == NULL) { 50913 return ma_vec3f_init_3f(0, 0, -1); 50914 } 50915 50916 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 50917 } 50918 50919 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) 50920 { 50921 if (pSpatializer == NULL) { 50922 return; 50923 } 50924 50925 ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); 50926 } 50927 50928 MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) 50929 { 50930 if (pSpatializer == NULL) { 50931 return ma_vec3f_init_3f(0, 0, 0); 50932 } 50933 50934 return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ 50935 } 50936 50937 MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) 50938 { 50939 if (pRelativePos != NULL) { 50940 pRelativePos->x = 0; 50941 pRelativePos->y = 0; 50942 pRelativePos->z = 0; 50943 } 50944 50945 if (pRelativeDir != NULL) { 50946 pRelativeDir->x = 0; 50947 pRelativeDir->y = 0; 50948 pRelativeDir->z = -1; 50949 } 50950 50951 if (pSpatializer == NULL) { 50952 return; 50953 } 50954 50955 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { 50956 /* There's no listener or we're using relative positioning. */ 50957 if (pRelativePos != NULL) { 50958 *pRelativePos = ma_spatializer_get_position(pSpatializer); 50959 } 50960 if (pRelativeDir != NULL) { 50961 *pRelativeDir = ma_spatializer_get_direction(pSpatializer); 50962 } 50963 } else { 50964 ma_vec3f spatializerPosition; 50965 ma_vec3f spatializerDirection; 50966 ma_vec3f listenerPosition; 50967 ma_vec3f listenerDirection; 50968 ma_vec3f v; 50969 ma_vec3f axisX; 50970 ma_vec3f axisY; 50971 ma_vec3f axisZ; 50972 float m[4][4]; 50973 50974 spatializerPosition = ma_spatializer_get_position(pSpatializer); 50975 spatializerDirection = ma_spatializer_get_direction(pSpatializer); 50976 listenerPosition = ma_spatializer_listener_get_position(pListener); 50977 listenerDirection = ma_spatializer_listener_get_direction(pListener); 50978 50979 /* 50980 We need to calcualte the right vector from our forward and up vectors. This is done with 50981 a cross product. 50982 */ 50983 axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ 50984 axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ 50985 50986 /* 50987 The calculation of axisX above can result in a zero-length vector if the listener is 50988 looking straight up on the Y axis. We'll need to fall back to a +X in this case so that 50989 the calculations below don't fall apart. This is where a quaternion based listener and 50990 sound orientation would come in handy. 50991 */ 50992 if (ma_vec3f_len2(axisX) == 0) { 50993 axisX = ma_vec3f_init_3f(1, 0, 0); 50994 } 50995 50996 axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ 50997 50998 /* 50999 We need to swap the X axis if we're left handed because otherwise the cross product above 51000 will have resulted in it pointing in the wrong direction (right handed was assumed in the 51001 cross products above). 51002 */ 51003 if (pListener->config.handedness == ma_handedness_left) { 51004 axisX = ma_vec3f_neg(axisX); 51005 } 51006 51007 /* Lookat. */ 51008 m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); 51009 m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); 51010 m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); 51011 m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; 51012 51013 /* 51014 Multiply the lookat matrix by the spatializer position to transform it to listener 51015 space. This allows calculations to work based on the sound being relative to the 51016 origin which makes things simpler. 51017 */ 51018 if (pRelativePos != NULL) { 51019 v = spatializerPosition; 51020 pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; 51021 pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; 51022 pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; 51023 } 51024 51025 /* 51026 The direction of the sound needs to also be transformed so that it's relative to the 51027 rotation of the listener. 51028 */ 51029 if (pRelativeDir != NULL) { 51030 v = spatializerDirection; 51031 pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; 51032 pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; 51033 pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; 51034 } 51035 } 51036 } 51037 51038 51039 51040 51041 /************************************************************************************************************************************************************** 51042 51043 Resampling 51044 51045 **************************************************************************************************************************************************************/ 51046 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 51047 { 51048 ma_linear_resampler_config config; 51049 MA_ZERO_OBJECT(&config); 51050 config.format = format; 51051 config.channels = channels; 51052 config.sampleRateIn = sampleRateIn; 51053 config.sampleRateOut = sampleRateOut; 51054 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); 51055 config.lpfNyquistFactor = 1; 51056 51057 return config; 51058 } 51059 51060 51061 typedef struct 51062 { 51063 size_t sizeInBytes; 51064 size_t x0Offset; 51065 size_t x1Offset; 51066 size_t lpfOffset; 51067 } ma_linear_resampler_heap_layout; 51068 51069 51070 static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) 51071 { 51072 /* 51073 So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will 51074 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. 51075 */ 51076 ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ 51077 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; 51078 51079 pResampler->inTimeFrac = 51080 (oldRateTimeWhole * newSampleRateOut) + 51081 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); 51082 51083 /* Make sure the fractional part is less than the output sample rate. */ 51084 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; 51085 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; 51086 } 51087 51088 static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) 51089 { 51090 ma_result result; 51091 ma_uint32 gcf; 51092 ma_uint32 lpfSampleRate; 51093 double lpfCutoffFrequency; 51094 ma_lpf_config lpfConfig; 51095 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ 51096 51097 if (pResampler == NULL) { 51098 return MA_INVALID_ARGS; 51099 } 51100 51101 if (sampleRateIn == 0 || sampleRateOut == 0) { 51102 return MA_INVALID_ARGS; 51103 } 51104 51105 oldSampleRateOut = pResampler->config.sampleRateOut; 51106 51107 pResampler->config.sampleRateIn = sampleRateIn; 51108 pResampler->config.sampleRateOut = sampleRateOut; 51109 51110 /* Simplify the sample rate. */ 51111 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); 51112 pResampler->config.sampleRateIn /= gcf; 51113 pResampler->config.sampleRateOut /= gcf; 51114 51115 /* Always initialize the low-pass filter, even when the order is 0. */ 51116 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { 51117 return MA_INVALID_ARGS; 51118 } 51119 51120 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); 51121 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); 51122 51123 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); 51124 51125 /* 51126 If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames 51127 getting cleared. Instead we re-initialize the filter which will maintain any cached frames. 51128 */ 51129 if (isResamplerAlreadyInitialized) { 51130 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); 51131 } else { 51132 result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); 51133 } 51134 51135 if (result != MA_SUCCESS) { 51136 return result; 51137 } 51138 51139 51140 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; 51141 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; 51142 51143 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ 51144 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); 51145 51146 return MA_SUCCESS; 51147 } 51148 51149 static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) 51150 { 51151 MA_ASSERT(pHeapLayout != NULL); 51152 51153 MA_ZERO_OBJECT(pHeapLayout); 51154 51155 if (pConfig == NULL) { 51156 return MA_INVALID_ARGS; 51157 } 51158 51159 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { 51160 return MA_INVALID_ARGS; 51161 } 51162 51163 if (pConfig->channels == 0) { 51164 return MA_INVALID_ARGS; 51165 } 51166 51167 pHeapLayout->sizeInBytes = 0; 51168 51169 /* x0 */ 51170 pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; 51171 if (pConfig->format == ma_format_f32) { 51172 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; 51173 } else { 51174 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; 51175 } 51176 51177 /* x1 */ 51178 pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; 51179 if (pConfig->format == ma_format_f32) { 51180 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; 51181 } else { 51182 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; 51183 } 51184 51185 /* LPF */ 51186 pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); 51187 { 51188 ma_result result; 51189 size_t lpfHeapSizeInBytes; 51190 ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ 51191 51192 result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); 51193 if (result != MA_SUCCESS) { 51194 return result; 51195 } 51196 51197 pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; 51198 } 51199 51200 /* Make sure allocation size is aligned. */ 51201 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 51202 51203 return MA_SUCCESS; 51204 } 51205 51206 MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) 51207 { 51208 ma_result result; 51209 ma_linear_resampler_heap_layout heapLayout; 51210 51211 if (pHeapSizeInBytes == NULL) { 51212 return MA_INVALID_ARGS; 51213 } 51214 51215 *pHeapSizeInBytes = 0; 51216 51217 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); 51218 if (result != MA_SUCCESS) { 51219 return result; 51220 } 51221 51222 *pHeapSizeInBytes = heapLayout.sizeInBytes; 51223 51224 return MA_SUCCESS; 51225 } 51226 51227 MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) 51228 { 51229 ma_result result; 51230 ma_linear_resampler_heap_layout heapLayout; 51231 51232 if (pResampler == NULL) { 51233 return MA_INVALID_ARGS; 51234 } 51235 51236 MA_ZERO_OBJECT(pResampler); 51237 51238 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); 51239 if (result != MA_SUCCESS) { 51240 return result; 51241 } 51242 51243 pResampler->config = *pConfig; 51244 51245 pResampler->_pHeap = pHeap; 51246 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 51247 51248 if (pConfig->format == ma_format_f32) { 51249 pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); 51250 pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); 51251 } else { 51252 pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); 51253 pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); 51254 } 51255 51256 /* Setting the rate will set up the filter and time advances for us. */ 51257 result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); 51258 if (result != MA_SUCCESS) { 51259 return result; 51260 } 51261 51262 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ 51263 pResampler->inTimeFrac = 0; 51264 51265 return MA_SUCCESS; 51266 } 51267 51268 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) 51269 { 51270 ma_result result; 51271 size_t heapSizeInBytes; 51272 void* pHeap; 51273 51274 result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); 51275 if (result != MA_SUCCESS) { 51276 return result; 51277 } 51278 51279 if (heapSizeInBytes > 0) { 51280 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 51281 if (pHeap == NULL) { 51282 return MA_OUT_OF_MEMORY; 51283 } 51284 } else { 51285 pHeap = NULL; 51286 } 51287 51288 result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); 51289 if (result != MA_SUCCESS) { 51290 ma_free(pHeap, pAllocationCallbacks); 51291 return result; 51292 } 51293 51294 pResampler->_ownsHeap = MA_TRUE; 51295 return MA_SUCCESS; 51296 } 51297 51298 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) 51299 { 51300 if (pResampler == NULL) { 51301 return; 51302 } 51303 51304 ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); 51305 51306 if (pResampler->_ownsHeap) { 51307 ma_free(pResampler->_pHeap, pAllocationCallbacks); 51308 } 51309 } 51310 51311 static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) 51312 { 51313 ma_int32 b; 51314 ma_int32 c; 51315 ma_int32 r; 51316 51317 MA_ASSERT(a <= (1<<shift)); 51318 51319 b = x * ((1<<shift) - a); 51320 c = y * a; 51321 r = b + c; 51322 51323 return (ma_int16)(r >> shift); 51324 } 51325 51326 static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) 51327 { 51328 ma_uint32 c; 51329 ma_uint32 a; 51330 const ma_uint32 channels = pResampler->config.channels; 51331 const ma_uint32 shift = 12; 51332 51333 MA_ASSERT(pResampler != NULL); 51334 MA_ASSERT(pFrameOut != NULL); 51335 51336 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; 51337 51338 MA_ASSUME(channels > 0); 51339 for (c = 0; c < channels; c += 1) { 51340 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); 51341 pFrameOut[c] = s; 51342 } 51343 } 51344 51345 51346 static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) 51347 { 51348 ma_uint32 c; 51349 float a; 51350 const ma_uint32 channels = pResampler->config.channels; 51351 51352 MA_ASSERT(pResampler != NULL); 51353 MA_ASSERT(pFrameOut != NULL); 51354 51355 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; 51356 51357 MA_ASSUME(channels > 0); 51358 for (c = 0; c < channels; c += 1) { 51359 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); 51360 pFrameOut[c] = s; 51361 } 51362 } 51363 51364 static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51365 { 51366 const ma_int16* pFramesInS16; 51367 /* */ ma_int16* pFramesOutS16; 51368 ma_uint64 frameCountIn; 51369 ma_uint64 frameCountOut; 51370 ma_uint64 framesProcessedIn; 51371 ma_uint64 framesProcessedOut; 51372 51373 MA_ASSERT(pResampler != NULL); 51374 MA_ASSERT(pFrameCountIn != NULL); 51375 MA_ASSERT(pFrameCountOut != NULL); 51376 51377 pFramesInS16 = (const ma_int16*)pFramesIn; 51378 pFramesOutS16 = ( ma_int16*)pFramesOut; 51379 frameCountIn = *pFrameCountIn; 51380 frameCountOut = *pFrameCountOut; 51381 framesProcessedIn = 0; 51382 framesProcessedOut = 0; 51383 51384 while (framesProcessedOut < frameCountOut) { 51385 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ 51386 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { 51387 ma_uint32 iChannel; 51388 51389 if (pFramesInS16 != NULL) { 51390 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51391 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; 51392 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; 51393 } 51394 pFramesInS16 += pResampler->config.channels; 51395 } else { 51396 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51397 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; 51398 pResampler->x1.s16[iChannel] = 0; 51399 } 51400 } 51401 51402 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ 51403 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { 51404 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); 51405 } 51406 51407 framesProcessedIn += 1; 51408 pResampler->inTimeInt -= 1; 51409 } 51410 51411 if (pResampler->inTimeInt > 0) { 51412 break; /* Ran out of input data. */ 51413 } 51414 51415 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ 51416 if (pFramesOutS16 != NULL) { 51417 MA_ASSERT(pResampler->inTimeInt == 0); 51418 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); 51419 51420 pFramesOutS16 += pResampler->config.channels; 51421 } 51422 51423 framesProcessedOut += 1; 51424 51425 /* Advance time forward. */ 51426 pResampler->inTimeInt += pResampler->inAdvanceInt; 51427 pResampler->inTimeFrac += pResampler->inAdvanceFrac; 51428 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { 51429 pResampler->inTimeFrac -= pResampler->config.sampleRateOut; 51430 pResampler->inTimeInt += 1; 51431 } 51432 } 51433 51434 *pFrameCountIn = framesProcessedIn; 51435 *pFrameCountOut = framesProcessedOut; 51436 51437 return MA_SUCCESS; 51438 } 51439 51440 static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51441 { 51442 const ma_int16* pFramesInS16; 51443 /* */ ma_int16* pFramesOutS16; 51444 ma_uint64 frameCountIn; 51445 ma_uint64 frameCountOut; 51446 ma_uint64 framesProcessedIn; 51447 ma_uint64 framesProcessedOut; 51448 51449 MA_ASSERT(pResampler != NULL); 51450 MA_ASSERT(pFrameCountIn != NULL); 51451 MA_ASSERT(pFrameCountOut != NULL); 51452 51453 pFramesInS16 = (const ma_int16*)pFramesIn; 51454 pFramesOutS16 = ( ma_int16*)pFramesOut; 51455 frameCountIn = *pFrameCountIn; 51456 frameCountOut = *pFrameCountOut; 51457 framesProcessedIn = 0; 51458 framesProcessedOut = 0; 51459 51460 while (framesProcessedOut < frameCountOut) { 51461 /* Before interpolating we need to load the buffers. */ 51462 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { 51463 ma_uint32 iChannel; 51464 51465 if (pFramesInS16 != NULL) { 51466 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51467 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; 51468 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; 51469 } 51470 pFramesInS16 += pResampler->config.channels; 51471 } else { 51472 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51473 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; 51474 pResampler->x1.s16[iChannel] = 0; 51475 } 51476 } 51477 51478 framesProcessedIn += 1; 51479 pResampler->inTimeInt -= 1; 51480 } 51481 51482 if (pResampler->inTimeInt > 0) { 51483 break; /* Ran out of input data. */ 51484 } 51485 51486 /* Getting here means the frames have been loaded and we can generate the next output frame. */ 51487 if (pFramesOutS16 != NULL) { 51488 MA_ASSERT(pResampler->inTimeInt == 0); 51489 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); 51490 51491 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ 51492 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { 51493 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); 51494 } 51495 51496 pFramesOutS16 += pResampler->config.channels; 51497 } 51498 51499 framesProcessedOut += 1; 51500 51501 /* Advance time forward. */ 51502 pResampler->inTimeInt += pResampler->inAdvanceInt; 51503 pResampler->inTimeFrac += pResampler->inAdvanceFrac; 51504 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { 51505 pResampler->inTimeFrac -= pResampler->config.sampleRateOut; 51506 pResampler->inTimeInt += 1; 51507 } 51508 } 51509 51510 *pFrameCountIn = framesProcessedIn; 51511 *pFrameCountOut = framesProcessedOut; 51512 51513 return MA_SUCCESS; 51514 } 51515 51516 static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51517 { 51518 MA_ASSERT(pResampler != NULL); 51519 51520 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { 51521 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51522 } else { 51523 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51524 } 51525 } 51526 51527 51528 static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51529 { 51530 const float* pFramesInF32; 51531 /* */ float* pFramesOutF32; 51532 ma_uint64 frameCountIn; 51533 ma_uint64 frameCountOut; 51534 ma_uint64 framesProcessedIn; 51535 ma_uint64 framesProcessedOut; 51536 51537 MA_ASSERT(pResampler != NULL); 51538 MA_ASSERT(pFrameCountIn != NULL); 51539 MA_ASSERT(pFrameCountOut != NULL); 51540 51541 pFramesInF32 = (const float*)pFramesIn; 51542 pFramesOutF32 = ( float*)pFramesOut; 51543 frameCountIn = *pFrameCountIn; 51544 frameCountOut = *pFrameCountOut; 51545 framesProcessedIn = 0; 51546 framesProcessedOut = 0; 51547 51548 while (framesProcessedOut < frameCountOut) { 51549 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ 51550 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { 51551 ma_uint32 iChannel; 51552 51553 if (pFramesInF32 != NULL) { 51554 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51555 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; 51556 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; 51557 } 51558 pFramesInF32 += pResampler->config.channels; 51559 } else { 51560 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51561 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; 51562 pResampler->x1.f32[iChannel] = 0; 51563 } 51564 } 51565 51566 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ 51567 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { 51568 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); 51569 } 51570 51571 framesProcessedIn += 1; 51572 pResampler->inTimeInt -= 1; 51573 } 51574 51575 if (pResampler->inTimeInt > 0) { 51576 break; /* Ran out of input data. */ 51577 } 51578 51579 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ 51580 if (pFramesOutF32 != NULL) { 51581 MA_ASSERT(pResampler->inTimeInt == 0); 51582 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); 51583 51584 pFramesOutF32 += pResampler->config.channels; 51585 } 51586 51587 framesProcessedOut += 1; 51588 51589 /* Advance time forward. */ 51590 pResampler->inTimeInt += pResampler->inAdvanceInt; 51591 pResampler->inTimeFrac += pResampler->inAdvanceFrac; 51592 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { 51593 pResampler->inTimeFrac -= pResampler->config.sampleRateOut; 51594 pResampler->inTimeInt += 1; 51595 } 51596 } 51597 51598 *pFrameCountIn = framesProcessedIn; 51599 *pFrameCountOut = framesProcessedOut; 51600 51601 return MA_SUCCESS; 51602 } 51603 51604 static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51605 { 51606 const float* pFramesInF32; 51607 /* */ float* pFramesOutF32; 51608 ma_uint64 frameCountIn; 51609 ma_uint64 frameCountOut; 51610 ma_uint64 framesProcessedIn; 51611 ma_uint64 framesProcessedOut; 51612 51613 MA_ASSERT(pResampler != NULL); 51614 MA_ASSERT(pFrameCountIn != NULL); 51615 MA_ASSERT(pFrameCountOut != NULL); 51616 51617 pFramesInF32 = (const float*)pFramesIn; 51618 pFramesOutF32 = ( float*)pFramesOut; 51619 frameCountIn = *pFrameCountIn; 51620 frameCountOut = *pFrameCountOut; 51621 framesProcessedIn = 0; 51622 framesProcessedOut = 0; 51623 51624 while (framesProcessedOut < frameCountOut) { 51625 /* Before interpolating we need to load the buffers. */ 51626 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { 51627 ma_uint32 iChannel; 51628 51629 if (pFramesInF32 != NULL) { 51630 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51631 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; 51632 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; 51633 } 51634 pFramesInF32 += pResampler->config.channels; 51635 } else { 51636 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51637 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; 51638 pResampler->x1.f32[iChannel] = 0; 51639 } 51640 } 51641 51642 framesProcessedIn += 1; 51643 pResampler->inTimeInt -= 1; 51644 } 51645 51646 if (pResampler->inTimeInt > 0) { 51647 break; /* Ran out of input data. */ 51648 } 51649 51650 /* Getting here means the frames have been loaded and we can generate the next output frame. */ 51651 if (pFramesOutF32 != NULL) { 51652 MA_ASSERT(pResampler->inTimeInt == 0); 51653 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); 51654 51655 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ 51656 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { 51657 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); 51658 } 51659 51660 pFramesOutF32 += pResampler->config.channels; 51661 } 51662 51663 framesProcessedOut += 1; 51664 51665 /* Advance time forward. */ 51666 pResampler->inTimeInt += pResampler->inAdvanceInt; 51667 pResampler->inTimeFrac += pResampler->inAdvanceFrac; 51668 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { 51669 pResampler->inTimeFrac -= pResampler->config.sampleRateOut; 51670 pResampler->inTimeInt += 1; 51671 } 51672 } 51673 51674 *pFrameCountIn = framesProcessedIn; 51675 *pFrameCountOut = framesProcessedOut; 51676 51677 return MA_SUCCESS; 51678 } 51679 51680 static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51681 { 51682 MA_ASSERT(pResampler != NULL); 51683 51684 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { 51685 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51686 } else { 51687 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51688 } 51689 } 51690 51691 51692 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51693 { 51694 if (pResampler == NULL) { 51695 return MA_INVALID_ARGS; 51696 } 51697 51698 /* */ if (pResampler->config.format == ma_format_s16) { 51699 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51700 } else if (pResampler->config.format == ma_format_f32) { 51701 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51702 } else { 51703 /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ 51704 MA_ASSERT(MA_FALSE); 51705 return MA_INVALID_ARGS; 51706 } 51707 } 51708 51709 51710 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 51711 { 51712 return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); 51713 } 51714 51715 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) 51716 { 51717 ma_uint32 n; 51718 ma_uint32 d; 51719 51720 if (pResampler == NULL) { 51721 return MA_INVALID_ARGS; 51722 } 51723 51724 if (ratioInOut <= 0) { 51725 return MA_INVALID_ARGS; 51726 } 51727 51728 d = 1000000; 51729 n = (ma_uint32)(ratioInOut * d); 51730 51731 if (n == 0) { 51732 return MA_INVALID_ARGS; /* Ratio too small. */ 51733 } 51734 51735 MA_ASSERT(n != 0); 51736 51737 return ma_linear_resampler_set_rate(pResampler, n, d); 51738 } 51739 51740 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) 51741 { 51742 if (pResampler == NULL) { 51743 return 0; 51744 } 51745 51746 return 1 + ma_lpf_get_latency(&pResampler->lpf); 51747 } 51748 51749 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) 51750 { 51751 if (pResampler == NULL) { 51752 return 0; 51753 } 51754 51755 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; 51756 } 51757 51758 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) 51759 { 51760 ma_uint64 inputFrameCount; 51761 51762 if (pInputFrameCount == NULL) { 51763 return MA_INVALID_ARGS; 51764 } 51765 51766 *pInputFrameCount = 0; 51767 51768 if (pResampler == NULL) { 51769 return MA_INVALID_ARGS; 51770 } 51771 51772 if (outputFrameCount == 0) { 51773 return MA_SUCCESS; 51774 } 51775 51776 /* Any whole input frames are consumed before the first output frame is generated. */ 51777 inputFrameCount = pResampler->inTimeInt; 51778 outputFrameCount -= 1; 51779 51780 /* The rest of the output frames can be calculated in constant time. */ 51781 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; 51782 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; 51783 51784 *pInputFrameCount = inputFrameCount; 51785 51786 return MA_SUCCESS; 51787 } 51788 51789 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) 51790 { 51791 ma_uint64 outputFrameCount; 51792 ma_uint64 preliminaryInputFrameCountFromFrac; 51793 ma_uint64 preliminaryInputFrameCount; 51794 51795 if (pOutputFrameCount == NULL) { 51796 return MA_INVALID_ARGS; 51797 } 51798 51799 *pOutputFrameCount = 0; 51800 51801 if (pResampler == NULL) { 51802 return MA_INVALID_ARGS; 51803 } 51804 51805 /* 51806 The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to 51807 determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't 51808 be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation 51809 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. 51810 */ 51811 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; 51812 51813 /* 51814 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is 51815 used in the logic below to determine whether or not we need to add an extra output frame. 51816 */ 51817 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; 51818 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; 51819 51820 /* 51821 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than 51822 the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data 51823 to actually process. Otherwise we need to add the extra output frame. 51824 */ 51825 if (preliminaryInputFrameCount <= inputFrameCount) { 51826 outputFrameCount += 1; 51827 } 51828 51829 *pOutputFrameCount = outputFrameCount; 51830 51831 return MA_SUCCESS; 51832 } 51833 51834 MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) 51835 { 51836 ma_uint32 iChannel; 51837 51838 if (pResampler == NULL) { 51839 return MA_INVALID_ARGS; 51840 } 51841 51842 /* Timers need to be cleared back to zero. */ 51843 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ 51844 pResampler->inTimeFrac = 0; 51845 51846 /* Cached samples need to be cleared. */ 51847 if (pResampler->config.format == ma_format_f32) { 51848 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51849 pResampler->x0.f32[iChannel] = 0; 51850 pResampler->x1.f32[iChannel] = 0; 51851 } 51852 } else { 51853 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { 51854 pResampler->x0.s16[iChannel] = 0; 51855 pResampler->x1.s16[iChannel] = 0; 51856 } 51857 } 51858 51859 /* The low pass filter needs to have it's cache reset. */ 51860 ma_lpf_clear_cache(&pResampler->lpf); 51861 51862 return MA_SUCCESS; 51863 } 51864 51865 51866 51867 /* Linear resampler backend vtable. */ 51868 static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) 51869 { 51870 ma_linear_resampler_config linearConfig; 51871 51872 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); 51873 linearConfig.lpfOrder = pConfig->linear.lpfOrder; 51874 51875 return linearConfig; 51876 } 51877 51878 static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) 51879 { 51880 ma_linear_resampler_config linearConfig; 51881 51882 (void)pUserData; 51883 51884 linearConfig = ma_resampling_backend_get_config__linear(pConfig); 51885 51886 return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); 51887 } 51888 51889 static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) 51890 { 51891 ma_resampler* pResampler = (ma_resampler*)pUserData; 51892 ma_result result; 51893 ma_linear_resampler_config linearConfig; 51894 51895 (void)pUserData; 51896 51897 linearConfig = ma_resampling_backend_get_config__linear(pConfig); 51898 51899 result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); 51900 if (result != MA_SUCCESS) { 51901 return result; 51902 } 51903 51904 *ppBackend = &pResampler->state.linear; 51905 51906 return MA_SUCCESS; 51907 } 51908 51909 static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) 51910 { 51911 (void)pUserData; 51912 51913 ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); 51914 } 51915 51916 static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 51917 { 51918 (void)pUserData; 51919 51920 return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 51921 } 51922 51923 static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 51924 { 51925 (void)pUserData; 51926 51927 return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); 51928 } 51929 51930 static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) 51931 { 51932 (void)pUserData; 51933 51934 return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); 51935 } 51936 51937 static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) 51938 { 51939 (void)pUserData; 51940 51941 return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); 51942 } 51943 51944 static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) 51945 { 51946 (void)pUserData; 51947 51948 return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); 51949 } 51950 51951 static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) 51952 { 51953 (void)pUserData; 51954 51955 return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); 51956 } 51957 51958 static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) 51959 { 51960 (void)pUserData; 51961 51962 return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); 51963 } 51964 51965 static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = 51966 { 51967 ma_resampling_backend_get_heap_size__linear, 51968 ma_resampling_backend_init__linear, 51969 ma_resampling_backend_uninit__linear, 51970 ma_resampling_backend_process__linear, 51971 ma_resampling_backend_set_rate__linear, 51972 ma_resampling_backend_get_input_latency__linear, 51973 ma_resampling_backend_get_output_latency__linear, 51974 ma_resampling_backend_get_required_input_frame_count__linear, 51975 ma_resampling_backend_get_expected_output_frame_count__linear, 51976 ma_resampling_backend_reset__linear 51977 }; 51978 51979 51980 51981 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) 51982 { 51983 ma_resampler_config config; 51984 51985 MA_ZERO_OBJECT(&config); 51986 config.format = format; 51987 config.channels = channels; 51988 config.sampleRateIn = sampleRateIn; 51989 config.sampleRateOut = sampleRateOut; 51990 config.algorithm = algorithm; 51991 51992 /* Linear. */ 51993 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); 51994 51995 return config; 51996 } 51997 51998 static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) 51999 { 52000 MA_ASSERT(pConfig != NULL); 52001 MA_ASSERT(ppVTable != NULL); 52002 MA_ASSERT(ppUserData != NULL); 52003 52004 /* Safety. */ 52005 *ppVTable = NULL; 52006 *ppUserData = NULL; 52007 52008 switch (pConfig->algorithm) 52009 { 52010 case ma_resample_algorithm_linear: 52011 { 52012 *ppVTable = &g_ma_linear_resampler_vtable; 52013 *ppUserData = pResampler; 52014 } break; 52015 52016 case ma_resample_algorithm_custom: 52017 { 52018 *ppVTable = pConfig->pBackendVTable; 52019 *ppUserData = pConfig->pBackendUserData; 52020 } break; 52021 52022 default: return MA_INVALID_ARGS; 52023 } 52024 52025 return MA_SUCCESS; 52026 } 52027 52028 MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) 52029 { 52030 ma_result result; 52031 ma_resampling_backend_vtable* pVTable; 52032 void* pVTableUserData; 52033 52034 if (pHeapSizeInBytes == NULL) { 52035 return MA_INVALID_ARGS; 52036 } 52037 52038 *pHeapSizeInBytes = 0; 52039 52040 if (pConfig == NULL) { 52041 return MA_INVALID_ARGS; 52042 } 52043 52044 result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); 52045 if (result != MA_SUCCESS) { 52046 return result; 52047 } 52048 52049 if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { 52050 return MA_NOT_IMPLEMENTED; 52051 } 52052 52053 result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); 52054 if (result != MA_SUCCESS) { 52055 return result; 52056 } 52057 52058 return MA_SUCCESS; 52059 } 52060 52061 MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) 52062 { 52063 ma_result result; 52064 52065 if (pResampler == NULL) { 52066 return MA_INVALID_ARGS; 52067 } 52068 52069 MA_ZERO_OBJECT(pResampler); 52070 52071 if (pConfig == NULL) { 52072 return MA_INVALID_ARGS; 52073 } 52074 52075 pResampler->_pHeap = pHeap; 52076 pResampler->format = pConfig->format; 52077 pResampler->channels = pConfig->channels; 52078 pResampler->sampleRateIn = pConfig->sampleRateIn; 52079 pResampler->sampleRateOut = pConfig->sampleRateOut; 52080 52081 result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); 52082 if (result != MA_SUCCESS) { 52083 return result; 52084 } 52085 52086 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { 52087 return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ 52088 } 52089 52090 result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); 52091 if (result != MA_SUCCESS) { 52092 return result; 52093 } 52094 52095 return MA_SUCCESS; 52096 } 52097 52098 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) 52099 { 52100 ma_result result; 52101 size_t heapSizeInBytes; 52102 void* pHeap; 52103 52104 result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); 52105 if (result != MA_SUCCESS) { 52106 return result; 52107 } 52108 52109 if (heapSizeInBytes > 0) { 52110 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 52111 if (pHeap == NULL) { 52112 return MA_OUT_OF_MEMORY; 52113 } 52114 } else { 52115 pHeap = NULL; 52116 } 52117 52118 result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); 52119 if (result != MA_SUCCESS) { 52120 ma_free(pHeap, pAllocationCallbacks); 52121 return result; 52122 } 52123 52124 pResampler->_ownsHeap = MA_TRUE; 52125 return MA_SUCCESS; 52126 } 52127 52128 MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) 52129 { 52130 if (pResampler == NULL) { 52131 return; 52132 } 52133 52134 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { 52135 return; 52136 } 52137 52138 pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); 52139 52140 if (pResampler->_ownsHeap) { 52141 ma_free(pResampler->_pHeap, pAllocationCallbacks); 52142 } 52143 } 52144 52145 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 52146 { 52147 if (pResampler == NULL) { 52148 return MA_INVALID_ARGS; 52149 } 52150 52151 if (pFrameCountOut == NULL && pFrameCountIn == NULL) { 52152 return MA_INVALID_ARGS; 52153 } 52154 52155 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { 52156 return MA_NOT_IMPLEMENTED; 52157 } 52158 52159 return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 52160 } 52161 52162 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 52163 { 52164 ma_result result; 52165 52166 if (pResampler == NULL) { 52167 return MA_INVALID_ARGS; 52168 } 52169 52170 if (sampleRateIn == 0 || sampleRateOut == 0) { 52171 return MA_INVALID_ARGS; 52172 } 52173 52174 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { 52175 return MA_NOT_IMPLEMENTED; 52176 } 52177 52178 result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); 52179 if (result != MA_SUCCESS) { 52180 return result; 52181 } 52182 52183 pResampler->sampleRateIn = sampleRateIn; 52184 pResampler->sampleRateOut = sampleRateOut; 52185 52186 return MA_SUCCESS; 52187 } 52188 52189 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) 52190 { 52191 ma_uint32 n; 52192 ma_uint32 d; 52193 52194 if (pResampler == NULL) { 52195 return MA_INVALID_ARGS; 52196 } 52197 52198 if (ratio <= 0) { 52199 return MA_INVALID_ARGS; 52200 } 52201 52202 d = 1000; 52203 n = (ma_uint32)(ratio * d); 52204 52205 if (n == 0) { 52206 return MA_INVALID_ARGS; /* Ratio too small. */ 52207 } 52208 52209 MA_ASSERT(n != 0); 52210 52211 return ma_resampler_set_rate(pResampler, n, d); 52212 } 52213 52214 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) 52215 { 52216 if (pResampler == NULL) { 52217 return 0; 52218 } 52219 52220 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { 52221 return 0; 52222 } 52223 52224 return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); 52225 } 52226 52227 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) 52228 { 52229 if (pResampler == NULL) { 52230 return 0; 52231 } 52232 52233 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { 52234 return 0; 52235 } 52236 52237 return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); 52238 } 52239 52240 MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) 52241 { 52242 if (pInputFrameCount == NULL) { 52243 return MA_INVALID_ARGS; 52244 } 52245 52246 *pInputFrameCount = 0; 52247 52248 if (pResampler == NULL) { 52249 return MA_INVALID_ARGS; 52250 } 52251 52252 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { 52253 return MA_NOT_IMPLEMENTED; 52254 } 52255 52256 return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); 52257 } 52258 52259 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) 52260 { 52261 if (pOutputFrameCount == NULL) { 52262 return MA_INVALID_ARGS; 52263 } 52264 52265 *pOutputFrameCount = 0; 52266 52267 if (pResampler == NULL) { 52268 return MA_INVALID_ARGS; 52269 } 52270 52271 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { 52272 return MA_NOT_IMPLEMENTED; 52273 } 52274 52275 return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); 52276 } 52277 52278 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) 52279 { 52280 if (pResampler == NULL) { 52281 return MA_INVALID_ARGS; 52282 } 52283 52284 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { 52285 return MA_NOT_IMPLEMENTED; 52286 } 52287 52288 return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); 52289 } 52290 52291 /************************************************************************************************************************************************************** 52292 52293 Channel Conversion 52294 52295 **************************************************************************************************************************************************************/ 52296 #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 52297 #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 52298 #endif 52299 52300 #define MA_PLANE_LEFT 0 52301 #define MA_PLANE_RIGHT 1 52302 #define MA_PLANE_FRONT 2 52303 #define MA_PLANE_BACK 3 52304 #define MA_PLANE_BOTTOM 4 52305 #define MA_PLANE_TOP 5 52306 52307 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { 52308 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ 52309 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ 52310 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ 52311 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ 52312 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ 52313 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ 52314 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ 52315 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ 52316 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ 52317 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ 52318 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ 52319 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ 52320 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ 52321 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ 52322 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ 52323 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ 52324 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ 52325 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ 52326 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ 52327 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ 52328 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ 52329 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ 52330 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ 52331 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ 52332 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ 52333 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ 52334 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ 52335 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ 52336 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ 52337 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ 52338 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ 52339 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ 52340 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ 52341 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ 52342 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ 52343 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ 52344 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ 52345 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ 52346 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ 52347 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ 52348 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ 52349 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ 52350 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ 52351 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ 52352 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ 52353 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ 52354 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ 52355 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ 52356 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ 52357 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ 52358 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ 52359 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ 52360 }; 52361 52362 static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) 52363 { 52364 /* 52365 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to 52366 the following output configuration: 52367 52368 - front/left 52369 - side/left 52370 - back/left 52371 52372 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount 52373 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. 52374 52375 Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left 52376 speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted 52377 from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would 52378 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between 52379 the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works 52380 across 3 spatial dimensions. 52381 52382 The first thing to do is figure out how each speaker's volume is spread over each of plane: 52383 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane 52384 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane 52385 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane 52386 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane 52387 52388 The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other 52389 channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be 52390 taken by the other to produce the final contribution. 52391 */ 52392 52393 /* Contribution = Sum(Volume to Give * Volume to Take) */ 52394 float contribution = 52395 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + 52396 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + 52397 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + 52398 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + 52399 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + 52400 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; 52401 52402 return contribution; 52403 } 52404 52405 MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) 52406 { 52407 ma_channel_converter_config config; 52408 52409 MA_ZERO_OBJECT(&config); 52410 config.format = format; 52411 config.channelsIn = channelsIn; 52412 config.channelsOut = channelsOut; 52413 config.pChannelMapIn = pChannelMapIn; 52414 config.pChannelMapOut = pChannelMapOut; 52415 config.mixingMode = mixingMode; 52416 52417 return config; 52418 } 52419 52420 static ma_int32 ma_channel_converter_float_to_fixed(float x) 52421 { 52422 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT)); 52423 } 52424 52425 static ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels) 52426 { 52427 ma_uint32 spatialChannelCount = 0; 52428 ma_uint32 iChannel; 52429 52430 MA_ASSERT(pChannelMap != NULL); 52431 MA_ASSERT(channels > 0); 52432 52433 for (iChannel = 0; iChannel < channels; ++iChannel) { 52434 if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { 52435 spatialChannelCount++; 52436 } 52437 } 52438 52439 return spatialChannelCount; 52440 } 52441 52442 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) 52443 { 52444 int i; 52445 52446 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { 52447 return MA_FALSE; 52448 } 52449 52450 if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { 52451 return MA_FALSE; 52452 } 52453 52454 for (i = 0; i < 6; ++i) { /* Each side of a cube. */ 52455 if (g_maChannelPlaneRatios[channelPosition][i] != 0) { 52456 return MA_TRUE; 52457 } 52458 } 52459 52460 return MA_FALSE; 52461 } 52462 52463 52464 static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) 52465 { 52466 if (channelsOut == channelsIn) { 52467 return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); 52468 } else { 52469 return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ 52470 } 52471 } 52472 52473 static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) 52474 { 52475 if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { 52476 return ma_channel_conversion_path_passthrough; 52477 } 52478 52479 if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { 52480 return ma_channel_conversion_path_mono_out; 52481 } 52482 52483 if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { 52484 return ma_channel_conversion_path_mono_in; 52485 } 52486 52487 if (mode == ma_channel_mix_mode_custom_weights) { 52488 return ma_channel_conversion_path_weights; 52489 } 52490 52491 /* 52492 We can use a simple shuffle if both channel maps have the same channel count and all channel 52493 positions are present in both. 52494 */ 52495 if (channelsIn == channelsOut) { 52496 ma_uint32 iChannelIn; 52497 ma_bool32 areAllChannelPositionsPresent = MA_TRUE; 52498 for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { 52499 ma_bool32 isInputChannelPositionInOutput = MA_FALSE; 52500 if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { 52501 isInputChannelPositionInOutput = MA_TRUE; 52502 break; 52503 } 52504 52505 if (!isInputChannelPositionInOutput) { 52506 areAllChannelPositionsPresent = MA_FALSE; 52507 break; 52508 } 52509 } 52510 52511 if (areAllChannelPositionsPresent) { 52512 return ma_channel_conversion_path_shuffle; 52513 } 52514 } 52515 52516 /* Getting here means we'll need to use weights. */ 52517 return ma_channel_conversion_path_weights; 52518 } 52519 52520 52521 static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) 52522 { 52523 ma_uint32 iChannelIn; 52524 ma_uint32 iChannelOut; 52525 52526 if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { 52527 return MA_INVALID_ARGS; 52528 } 52529 52530 /* 52531 When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the 52532 input channel has more than one occurance of a channel position, the second one will be ignored. 52533 */ 52534 for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { 52535 ma_channel channelOut; 52536 52537 /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ 52538 pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; 52539 52540 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); 52541 for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { 52542 ma_channel channelIn; 52543 52544 channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); 52545 if (channelOut == channelIn) { 52546 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; 52547 break; 52548 } 52549 52550 /* 52551 Getting here means the channels don't exactly match, but we are going to support some 52552 relaxed matching for practicality. If, for example, there are two stereo channel maps, 52553 but one uses front left/right and the other uses side left/right, it makes logical 52554 sense to just map these. The way we'll do it is we'll check if there is a logical 52555 corresponding mapping, and if so, apply it, but we will *not* break from the loop, 52556 thereby giving the loop a chance to find an exact match later which will take priority. 52557 */ 52558 switch (channelOut) 52559 { 52560 /* Left channels. */ 52561 case MA_CHANNEL_FRONT_LEFT: 52562 case MA_CHANNEL_SIDE_LEFT: 52563 { 52564 switch (channelIn) { 52565 case MA_CHANNEL_FRONT_LEFT: 52566 case MA_CHANNEL_SIDE_LEFT: 52567 { 52568 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; 52569 } break; 52570 } 52571 } break; 52572 52573 /* Right channels. */ 52574 case MA_CHANNEL_FRONT_RIGHT: 52575 case MA_CHANNEL_SIDE_RIGHT: 52576 { 52577 switch (channelIn) { 52578 case MA_CHANNEL_FRONT_RIGHT: 52579 case MA_CHANNEL_SIDE_RIGHT: 52580 { 52581 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; 52582 } break; 52583 } 52584 } break; 52585 52586 default: break; 52587 } 52588 } 52589 } 52590 52591 return MA_SUCCESS; 52592 } 52593 52594 52595 static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) 52596 { 52597 ma_uint64 iFrame; 52598 ma_uint32 iChannelOut; 52599 52600 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52601 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52602 ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; 52603 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ 52604 pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; 52605 } else { 52606 pFramesOut[iChannelOut] = 0; 52607 } 52608 } 52609 52610 pFramesOut += channelsOut; 52611 pFramesIn += channelsIn; 52612 } 52613 } 52614 52615 static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) 52616 { 52617 ma_uint64 iFrame; 52618 ma_uint32 iChannelOut; 52619 52620 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52621 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52622 ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; 52623 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ 52624 pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; 52625 } else { 52626 pFramesOut[iChannelOut] = 0; 52627 } 52628 } 52629 52630 pFramesOut += channelsOut; 52631 pFramesIn += channelsIn; 52632 } 52633 } 52634 52635 static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) 52636 { 52637 ma_uint64 iFrame; 52638 ma_uint32 iChannelOut; 52639 52640 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52641 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52642 ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; 52643 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ 52644 pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; 52645 pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; 52646 pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; 52647 } else { 52648 pFramesOut[iChannelOut*3 + 0] = 0; 52649 } pFramesOut[iChannelOut*3 + 1] = 0; 52650 } pFramesOut[iChannelOut*3 + 2] = 0; 52651 52652 pFramesOut += channelsOut*3; 52653 pFramesIn += channelsIn*3; 52654 } 52655 } 52656 52657 static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) 52658 { 52659 ma_uint64 iFrame; 52660 ma_uint32 iChannelOut; 52661 52662 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52663 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52664 ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; 52665 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ 52666 pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; 52667 } else { 52668 pFramesOut[iChannelOut] = 0; 52669 } 52670 } 52671 52672 pFramesOut += channelsOut; 52673 pFramesIn += channelsIn; 52674 } 52675 } 52676 52677 static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) 52678 { 52679 ma_uint64 iFrame; 52680 ma_uint32 iChannelOut; 52681 52682 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52683 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52684 ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; 52685 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ 52686 pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; 52687 } else { 52688 pFramesOut[iChannelOut] = 0; 52689 } 52690 } 52691 52692 pFramesOut += channelsOut; 52693 pFramesIn += channelsIn; 52694 } 52695 } 52696 52697 static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) 52698 { 52699 if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { 52700 return MA_INVALID_ARGS; 52701 } 52702 52703 switch (format) 52704 { 52705 case ma_format_u8: 52706 { 52707 ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); 52708 } break; 52709 52710 case ma_format_s16: 52711 { 52712 ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); 52713 } break; 52714 52715 case ma_format_s24: 52716 { 52717 ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); 52718 } break; 52719 52720 case ma_format_s32: 52721 { 52722 ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); 52723 } break; 52724 52725 case ma_format_f32: 52726 { 52727 ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); 52728 } break; 52729 52730 default: return MA_INVALID_ARGS; /* Unknown format. */ 52731 } 52732 52733 return MA_SUCCESS; 52734 } 52735 52736 static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) 52737 { 52738 ma_uint64 iFrame; 52739 ma_uint32 iChannelIn; 52740 ma_uint32 accumulationCount; 52741 52742 if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { 52743 return MA_INVALID_ARGS; 52744 } 52745 52746 /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ 52747 52748 /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ 52749 accumulationCount = 0; 52750 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 52751 if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { 52752 accumulationCount += 1; 52753 } 52754 } 52755 52756 if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ 52757 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52758 float accumulation = 0; 52759 52760 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 52761 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); 52762 if (channelIn != MA_CHANNEL_NONE) { 52763 accumulation += pFramesIn[iChannelIn]; 52764 } 52765 } 52766 52767 pFramesOut[0] = accumulation / accumulationCount; 52768 pFramesOut += 1; 52769 pFramesIn += channelsIn; 52770 } 52771 } else { 52772 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); 52773 } 52774 52775 return MA_SUCCESS; 52776 } 52777 52778 static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) 52779 { 52780 ma_uint64 iFrame; 52781 ma_uint32 iChannelOut; 52782 52783 if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { 52784 return MA_INVALID_ARGS; 52785 } 52786 52787 /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ 52788 switch (monoExpansionMode) 52789 { 52790 case ma_mono_expansion_mode_average: 52791 { 52792 float weight; 52793 ma_uint32 validChannelCount = 0; 52794 52795 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52796 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52797 if (channelOut != MA_CHANNEL_NONE) { 52798 validChannelCount += 1; 52799 } 52800 } 52801 52802 weight = 1.0f / validChannelCount; 52803 52804 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52805 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52806 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52807 if (channelOut != MA_CHANNEL_NONE) { 52808 pFramesOut[iChannelOut] = pFramesIn[0] * weight; 52809 } 52810 } 52811 52812 pFramesOut += channelsOut; 52813 pFramesIn += 1; 52814 } 52815 } break; 52816 52817 case ma_mono_expansion_mode_stereo_only: 52818 { 52819 if (channelsOut >= 2) { 52820 ma_uint32 iChannelLeft = (ma_uint32)-1; 52821 ma_uint32 iChannelRight = (ma_uint32)-1; 52822 52823 /* 52824 We first need to find our stereo channels. We prefer front-left and front-right, but 52825 if they're not available, we'll also try side-left and side-right. If neither are 52826 available we'll fall through to the default case below. 52827 */ 52828 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52829 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52830 if (channelOut == MA_CHANNEL_SIDE_LEFT) { 52831 iChannelLeft = iChannelOut; 52832 } 52833 if (channelOut == MA_CHANNEL_SIDE_RIGHT) { 52834 iChannelRight = iChannelOut; 52835 } 52836 } 52837 52838 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52839 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52840 if (channelOut == MA_CHANNEL_FRONT_LEFT) { 52841 iChannelLeft = iChannelOut; 52842 } 52843 if (channelOut == MA_CHANNEL_FRONT_RIGHT) { 52844 iChannelRight = iChannelOut; 52845 } 52846 } 52847 52848 52849 if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { 52850 /* We found our stereo channels so we can duplicate the signal across those channels. */ 52851 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52852 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52853 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52854 if (channelOut != MA_CHANNEL_NONE) { 52855 if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { 52856 pFramesOut[iChannelOut] = pFramesIn[0]; 52857 } else { 52858 pFramesOut[iChannelOut] = 0.0f; 52859 } 52860 } 52861 } 52862 52863 pFramesOut += channelsOut; 52864 pFramesIn += 1; 52865 } 52866 52867 break; /* Get out of the switch. */ 52868 } else { 52869 /* Fallthrough. Does not have left and right channels. */ 52870 goto default_handler; 52871 } 52872 } else { 52873 /* Fallthrough. Does not have stereo channels. */ 52874 goto default_handler; 52875 } 52876 }; /* Fallthrough. See comments above. */ 52877 52878 case ma_mono_expansion_mode_duplicate: 52879 default: 52880 { 52881 default_handler: 52882 { 52883 if (channelsOut <= MA_MAX_CHANNELS) { 52884 ma_bool32 hasEmptyChannel = MA_FALSE; 52885 ma_channel channelPositions[MA_MAX_CHANNELS]; 52886 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52887 channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52888 if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { 52889 hasEmptyChannel = MA_TRUE; 52890 } 52891 } 52892 52893 if (hasEmptyChannel == MA_FALSE) { 52894 /* 52895 Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully 52896 help the compiler with auto-vectorization.m 52897 */ 52898 if (channelsOut == 2) { 52899 #if defined(MA_SUPPORT_SSE2) 52900 if (ma_has_sse2()) { 52901 /* We want to do two frames in each iteration. */ 52902 ma_uint64 unrolledFrameCount = frameCount >> 1; 52903 52904 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { 52905 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); 52906 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); 52907 _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); 52908 } 52909 52910 /* Tail. */ 52911 iFrame = unrolledFrameCount << 1; 52912 goto generic_on_fastpath; 52913 } else 52914 #endif 52915 { 52916 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52917 for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { 52918 pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; 52919 } 52920 } 52921 } 52922 } else if (channelsOut == 6) { 52923 #if defined(MA_SUPPORT_SSE2) 52924 if (ma_has_sse2()) { 52925 /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ 52926 ma_uint64 unrolledFrameCount = frameCount >> 1; 52927 52928 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { 52929 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); 52930 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); 52931 52932 _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); 52933 _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); 52934 _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); 52935 } 52936 52937 /* Tail. */ 52938 iFrame = unrolledFrameCount << 1; 52939 goto generic_on_fastpath; 52940 } else 52941 #endif 52942 { 52943 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52944 for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { 52945 pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; 52946 } 52947 } 52948 } 52949 } else if (channelsOut == 8) { 52950 #if defined(MA_SUPPORT_SSE2) 52951 if (ma_has_sse2()) { 52952 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52953 __m128 in = _mm_set1_ps(pFramesIn[iFrame]); 52954 _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); 52955 _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); 52956 } 52957 } else 52958 #endif 52959 { 52960 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52961 for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { 52962 pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; 52963 } 52964 } 52965 } 52966 } else { 52967 iFrame = 0; 52968 52969 #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ 52970 generic_on_fastpath: 52971 #endif 52972 { 52973 for (; iFrame < frameCount; iFrame += 1) { 52974 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52975 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; 52976 } 52977 } 52978 } 52979 } 52980 } else { 52981 /* Slow path. Need to handle MA_CHANNEL_NONE. */ 52982 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52983 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52984 if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { 52985 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; 52986 } 52987 } 52988 } 52989 } 52990 } else { 52991 /* Slow path. Too many channels to store on the stack. */ 52992 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 52993 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 52994 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 52995 if (channelOut != MA_CHANNEL_NONE) { 52996 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; 52997 } 52998 } 52999 } 53000 } 53001 } 53002 } break; 53003 } 53004 53005 return MA_SUCCESS; 53006 } 53007 53008 static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) 53009 { 53010 ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); 53011 53012 /* Optimized Path: Passthrough */ 53013 if (conversionPath == ma_channel_conversion_path_passthrough) { 53014 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); 53015 return; 53016 } 53017 53018 /* Special Path: Mono Output. */ 53019 if (conversionPath == ma_channel_conversion_path_mono_out) { 53020 ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); 53021 return; 53022 } 53023 53024 /* Special Path: Mono Input. */ 53025 if (conversionPath == ma_channel_conversion_path_mono_in) { 53026 ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); 53027 return; 53028 } 53029 53030 /* Getting here means we aren't running on an optimized conversion path. */ 53031 if (channelsOut <= MA_MAX_CHANNELS) { 53032 ma_result result; 53033 53034 if (mode == ma_channel_mix_mode_simple) { 53035 ma_channel shuffleTable[MA_MAX_CHANNELS]; 53036 53037 result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); 53038 if (result != MA_SUCCESS) { 53039 return; 53040 } 53041 53042 result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); 53043 if (result != MA_SUCCESS) { 53044 return; 53045 } 53046 } else { 53047 ma_uint32 iFrame; 53048 ma_uint32 iChannelOut; 53049 ma_uint32 iChannelIn; 53050 float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ 53051 53052 /* 53053 If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to 53054 fall back to a slower path because otherwise we'll run out of stack space. 53055 */ 53056 if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { 53057 /* Pre-compute weights. */ 53058 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 53059 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 53060 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 53061 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); 53062 weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); 53063 } 53064 } 53065 53066 iFrame = 0; 53067 53068 /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ 53069 if (channelsOut == 8) { 53070 /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ 53071 if (channelsIn == 2) { 53072 for (; iFrame < frameCount; iFrame += 1) { 53073 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 53074 53075 accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; 53076 accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; 53077 accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; 53078 accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; 53079 accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; 53080 accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; 53081 accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; 53082 accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; 53083 53084 accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; 53085 accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; 53086 accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; 53087 accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; 53088 accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; 53089 accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; 53090 accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; 53091 accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; 53092 53093 pFramesOut[iFrame*8 + 0] = accumulation[0]; 53094 pFramesOut[iFrame*8 + 1] = accumulation[1]; 53095 pFramesOut[iFrame*8 + 2] = accumulation[2]; 53096 pFramesOut[iFrame*8 + 3] = accumulation[3]; 53097 pFramesOut[iFrame*8 + 4] = accumulation[4]; 53098 pFramesOut[iFrame*8 + 5] = accumulation[5]; 53099 pFramesOut[iFrame*8 + 6] = accumulation[6]; 53100 pFramesOut[iFrame*8 + 7] = accumulation[7]; 53101 } 53102 } else { 53103 /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ 53104 for (; iFrame < frameCount; iFrame += 1) { 53105 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 53106 53107 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 53108 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; 53109 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; 53110 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; 53111 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; 53112 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; 53113 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; 53114 accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; 53115 accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; 53116 } 53117 53118 pFramesOut[iFrame*8 + 0] = accumulation[0]; 53119 pFramesOut[iFrame*8 + 1] = accumulation[1]; 53120 pFramesOut[iFrame*8 + 2] = accumulation[2]; 53121 pFramesOut[iFrame*8 + 3] = accumulation[3]; 53122 pFramesOut[iFrame*8 + 4] = accumulation[4]; 53123 pFramesOut[iFrame*8 + 5] = accumulation[5]; 53124 pFramesOut[iFrame*8 + 6] = accumulation[6]; 53125 pFramesOut[iFrame*8 + 7] = accumulation[7]; 53126 } 53127 } 53128 } else if (channelsOut == 6) { 53129 /* 53130 When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll 53131 expand our weights and do two frames at a time. 53132 */ 53133 for (; iFrame < frameCount; iFrame += 1) { 53134 float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 53135 53136 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 53137 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; 53138 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; 53139 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; 53140 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; 53141 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; 53142 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; 53143 } 53144 53145 pFramesOut[iFrame*6 + 0] = accumulation[0]; 53146 pFramesOut[iFrame*6 + 1] = accumulation[1]; 53147 pFramesOut[iFrame*6 + 2] = accumulation[2]; 53148 pFramesOut[iFrame*6 + 3] = accumulation[3]; 53149 pFramesOut[iFrame*6 + 4] = accumulation[4]; 53150 pFramesOut[iFrame*6 + 5] = accumulation[5]; 53151 } 53152 } 53153 53154 /* Leftover frames. */ 53155 for (; iFrame < frameCount; iFrame += 1) { 53156 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 53157 float accumulation = 0; 53158 53159 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 53160 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; 53161 } 53162 53163 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; 53164 } 53165 } 53166 } else { 53167 /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ 53168 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53169 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { 53170 float accumulation = 0; 53171 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); 53172 53173 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { 53174 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); 53175 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); 53176 } 53177 53178 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; 53179 } 53180 } 53181 } 53182 } 53183 } else { 53184 /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ 53185 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); 53186 } 53187 } 53188 53189 53190 typedef struct 53191 { 53192 size_t sizeInBytes; 53193 size_t channelMapInOffset; 53194 size_t channelMapOutOffset; 53195 size_t shuffleTableOffset; 53196 size_t weightsOffset; 53197 } ma_channel_converter_heap_layout; 53198 53199 static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) 53200 { 53201 return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); 53202 } 53203 53204 static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) 53205 { 53206 ma_channel_conversion_path conversionPath; 53207 53208 MA_ASSERT(pHeapLayout != NULL); 53209 53210 if (pConfig == NULL) { 53211 return MA_INVALID_ARGS; 53212 } 53213 53214 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { 53215 return MA_INVALID_ARGS; 53216 } 53217 53218 if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { 53219 return MA_INVALID_ARGS; 53220 } 53221 53222 if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { 53223 return MA_INVALID_ARGS; 53224 } 53225 53226 pHeapLayout->sizeInBytes = 0; 53227 53228 /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ 53229 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; 53230 if (pConfig->pChannelMapIn != NULL) { 53231 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; 53232 } 53233 53234 /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ 53235 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; 53236 if (pConfig->pChannelMapOut != NULL) { 53237 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; 53238 } 53239 53240 /* Alignment for the next section. */ 53241 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 53242 53243 /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ 53244 conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); 53245 53246 /* Shuffle table */ 53247 pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; 53248 if (conversionPath == ma_channel_conversion_path_shuffle) { 53249 pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; 53250 } 53251 53252 /* Weights */ 53253 pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; 53254 if (conversionPath == ma_channel_conversion_path_weights) { 53255 pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; 53256 pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; 53257 } 53258 53259 /* Make sure allocation size is aligned. */ 53260 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 53261 53262 return MA_SUCCESS; 53263 } 53264 53265 MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) 53266 { 53267 ma_result result; 53268 ma_channel_converter_heap_layout heapLayout; 53269 53270 if (pHeapSizeInBytes == NULL) { 53271 return MA_INVALID_ARGS; 53272 } 53273 53274 *pHeapSizeInBytes = 0; 53275 53276 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); 53277 if (result != MA_SUCCESS) { 53278 return result; 53279 } 53280 53281 *pHeapSizeInBytes = heapLayout.sizeInBytes; 53282 53283 return MA_SUCCESS; 53284 } 53285 53286 MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) 53287 { 53288 ma_result result; 53289 ma_channel_converter_heap_layout heapLayout; 53290 53291 if (pConverter == NULL) { 53292 return MA_INVALID_ARGS; 53293 } 53294 53295 MA_ZERO_OBJECT(pConverter); 53296 53297 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); 53298 if (result != MA_SUCCESS) { 53299 return result; 53300 } 53301 53302 pConverter->_pHeap = pHeap; 53303 MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); 53304 53305 pConverter->format = pConfig->format; 53306 pConverter->channelsIn = pConfig->channelsIn; 53307 pConverter->channelsOut = pConfig->channelsOut; 53308 pConverter->mixingMode = pConfig->mixingMode; 53309 53310 if (pConfig->pChannelMapIn != NULL) { 53311 pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); 53312 ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); 53313 } else { 53314 pConverter->pChannelMapIn = NULL; /* Use default channel map. */ 53315 } 53316 53317 if (pConfig->pChannelMapOut != NULL) { 53318 pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); 53319 ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); 53320 } else { 53321 pConverter->pChannelMapOut = NULL; /* Use default channel map. */ 53322 } 53323 53324 pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); 53325 53326 if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { 53327 pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); 53328 ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); 53329 } 53330 53331 if (pConverter->conversionPath == ma_channel_conversion_path_weights) { 53332 ma_uint32 iChannelIn; 53333 ma_uint32 iChannelOut; 53334 53335 if (pConverter->format == ma_format_f32) { 53336 pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); 53337 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { 53338 pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); 53339 } 53340 } else { 53341 pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); 53342 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { 53343 pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); 53344 } 53345 } 53346 53347 /* Silence our weights by default. */ 53348 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { 53349 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { 53350 if (pConverter->format == ma_format_f32) { 53351 pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; 53352 } else { 53353 pConverter->weights.s16[iChannelIn][iChannelOut] = 0; 53354 } 53355 } 53356 } 53357 53358 /* 53359 We now need to fill out our weights table. This is determined by the mixing mode. 53360 */ 53361 53362 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ 53363 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53364 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); 53365 53366 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53367 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); 53368 53369 if (channelPosIn == channelPosOut) { 53370 float weight = 1; 53371 53372 if (pConverter->format == ma_format_f32) { 53373 pConverter->weights.f32[iChannelIn][iChannelOut] = weight; 53374 } else { 53375 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); 53376 } 53377 } 53378 } 53379 } 53380 53381 switch (pConverter->mixingMode) 53382 { 53383 case ma_channel_mix_mode_custom_weights: 53384 { 53385 if (pConfig->ppWeights == NULL) { 53386 return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ 53387 } 53388 53389 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { 53390 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { 53391 float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; 53392 53393 if (pConverter->format == ma_format_f32) { 53394 pConverter->weights.f32[iChannelIn][iChannelOut] = weight; 53395 } else { 53396 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); 53397 } 53398 } 53399 } 53400 } break; 53401 53402 case ma_channel_mix_mode_simple: 53403 { 53404 /* 53405 In simple mode, only set weights for channels that have exactly matching types, leave the rest at 53406 zero. The 1:1 mappings have already been covered before this switch statement. 53407 */ 53408 } break; 53409 53410 case ma_channel_mix_mode_rectangular: 53411 default: 53412 { 53413 /* Unmapped input channels. */ 53414 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53415 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); 53416 53417 if (ma_is_spatial_channel_position(channelPosIn)) { 53418 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { 53419 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53420 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); 53421 53422 if (ma_is_spatial_channel_position(channelPosOut)) { 53423 float weight = 0; 53424 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { 53425 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); 53426 } 53427 53428 /* Only apply the weight if we haven't already got some contribution from the respective channels. */ 53429 if (pConverter->format == ma_format_f32) { 53430 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { 53431 pConverter->weights.f32[iChannelIn][iChannelOut] = weight; 53432 } 53433 } else { 53434 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { 53435 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); 53436 } 53437 } 53438 } 53439 } 53440 } 53441 } 53442 } 53443 53444 /* Unmapped output channels. */ 53445 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53446 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); 53447 53448 if (ma_is_spatial_channel_position(channelPosOut)) { 53449 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { 53450 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53451 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); 53452 53453 if (ma_is_spatial_channel_position(channelPosIn)) { 53454 float weight = 0; 53455 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { 53456 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); 53457 } 53458 53459 /* Only apply the weight if we haven't already got some contribution from the respective channels. */ 53460 if (pConverter->format == ma_format_f32) { 53461 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { 53462 pConverter->weights.f32[iChannelIn][iChannelOut] = weight; 53463 } 53464 } else { 53465 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { 53466 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); 53467 } 53468 } 53469 } 53470 } 53471 } 53472 } 53473 } 53474 53475 /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ 53476 if (pConfig->calculateLFEFromSpatialChannels) { 53477 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { 53478 ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); 53479 ma_uint32 iChannelOutLFE; 53480 53481 if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { 53482 const float weightForLFE = 1.0f / spatialChannelCount; 53483 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53484 const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); 53485 if (ma_is_spatial_channel_position(channelPosIn)) { 53486 if (pConverter->format == ma_format_f32) { 53487 if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { 53488 pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; 53489 } 53490 } else { 53491 if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { 53492 pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); 53493 } 53494 } 53495 } 53496 } 53497 } 53498 } 53499 } 53500 } break; 53501 } 53502 } 53503 53504 return MA_SUCCESS; 53505 } 53506 53507 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) 53508 { 53509 ma_result result; 53510 size_t heapSizeInBytes; 53511 void* pHeap; 53512 53513 result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); 53514 if (result != MA_SUCCESS) { 53515 return result; 53516 } 53517 53518 if (heapSizeInBytes > 0) { 53519 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 53520 if (pHeap == NULL) { 53521 return MA_OUT_OF_MEMORY; 53522 } 53523 } else { 53524 pHeap = NULL; 53525 } 53526 53527 result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); 53528 if (result != MA_SUCCESS) { 53529 ma_free(pHeap, pAllocationCallbacks); 53530 return result; 53531 } 53532 53533 pConverter->_ownsHeap = MA_TRUE; 53534 return MA_SUCCESS; 53535 } 53536 53537 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) 53538 { 53539 if (pConverter == NULL) { 53540 return; 53541 } 53542 53543 if (pConverter->_ownsHeap) { 53544 ma_free(pConverter->_pHeap, pAllocationCallbacks); 53545 } 53546 } 53547 53548 static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53549 { 53550 MA_ASSERT(pConverter != NULL); 53551 MA_ASSERT(pFramesOut != NULL); 53552 MA_ASSERT(pFramesIn != NULL); 53553 53554 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); 53555 return MA_SUCCESS; 53556 } 53557 53558 static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53559 { 53560 MA_ASSERT(pConverter != NULL); 53561 MA_ASSERT(pFramesOut != NULL); 53562 MA_ASSERT(pFramesIn != NULL); 53563 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); 53564 53565 return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); 53566 } 53567 53568 static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53569 { 53570 ma_uint64 iFrame; 53571 53572 MA_ASSERT(pConverter != NULL); 53573 MA_ASSERT(pFramesOut != NULL); 53574 MA_ASSERT(pFramesIn != NULL); 53575 MA_ASSERT(pConverter->channelsIn == 1); 53576 53577 switch (pConverter->format) 53578 { 53579 case ma_format_u8: 53580 { 53581 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; 53582 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; 53583 53584 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53585 ma_uint32 iChannel; 53586 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { 53587 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; 53588 } 53589 } 53590 } break; 53591 53592 case ma_format_s16: 53593 { 53594 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 53595 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 53596 53597 if (pConverter->channelsOut == 2) { 53598 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53599 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; 53600 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; 53601 } 53602 } else { 53603 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53604 ma_uint32 iChannel; 53605 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { 53606 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; 53607 } 53608 } 53609 } 53610 } break; 53611 53612 case ma_format_s24: 53613 { 53614 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; 53615 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; 53616 53617 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53618 ma_uint32 iChannel; 53619 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { 53620 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; 53621 ma_uint64 iSampleIn = iFrame; 53622 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; 53623 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; 53624 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; 53625 } 53626 } 53627 } break; 53628 53629 case ma_format_s32: 53630 { 53631 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; 53632 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; 53633 53634 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53635 ma_uint32 iChannel; 53636 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { 53637 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; 53638 } 53639 } 53640 } break; 53641 53642 case ma_format_f32: 53643 { 53644 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 53645 const float* pFramesInF32 = (const float*)pFramesIn; 53646 53647 if (pConverter->channelsOut == 2) { 53648 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53649 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; 53650 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; 53651 } 53652 } else { 53653 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53654 ma_uint32 iChannel; 53655 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { 53656 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; 53657 } 53658 } 53659 } 53660 } break; 53661 53662 default: return MA_INVALID_OPERATION; /* Unknown format. */ 53663 } 53664 53665 return MA_SUCCESS; 53666 } 53667 53668 static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53669 { 53670 ma_uint64 iFrame; 53671 ma_uint32 iChannel; 53672 53673 MA_ASSERT(pConverter != NULL); 53674 MA_ASSERT(pFramesOut != NULL); 53675 MA_ASSERT(pFramesIn != NULL); 53676 MA_ASSERT(pConverter->channelsOut == 1); 53677 53678 switch (pConverter->format) 53679 { 53680 case ma_format_u8: 53681 { 53682 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; 53683 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; 53684 53685 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53686 ma_int32 t = 0; 53687 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { 53688 t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); 53689 } 53690 53691 pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); 53692 } 53693 } break; 53694 53695 case ma_format_s16: 53696 { 53697 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 53698 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 53699 53700 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53701 ma_int32 t = 0; 53702 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { 53703 t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; 53704 } 53705 53706 pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); 53707 } 53708 } break; 53709 53710 case ma_format_s24: 53711 { 53712 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; 53713 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; 53714 53715 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53716 ma_int64 t = 0; 53717 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { 53718 t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); 53719 } 53720 53721 ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); 53722 } 53723 } break; 53724 53725 case ma_format_s32: 53726 { 53727 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; 53728 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; 53729 53730 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53731 ma_int64 t = 0; 53732 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { 53733 t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; 53734 } 53735 53736 pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); 53737 } 53738 } break; 53739 53740 case ma_format_f32: 53741 { 53742 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 53743 const float* pFramesInF32 = (const float*)pFramesIn; 53744 53745 for (iFrame = 0; iFrame < frameCount; ++iFrame) { 53746 float t = 0; 53747 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { 53748 t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; 53749 } 53750 53751 pFramesOutF32[iFrame] = t / pConverter->channelsIn; 53752 } 53753 } break; 53754 53755 default: return MA_INVALID_OPERATION; /* Unknown format. */ 53756 } 53757 53758 return MA_SUCCESS; 53759 } 53760 53761 static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53762 { 53763 ma_uint32 iFrame; 53764 ma_uint32 iChannelIn; 53765 ma_uint32 iChannelOut; 53766 53767 MA_ASSERT(pConverter != NULL); 53768 MA_ASSERT(pFramesOut != NULL); 53769 MA_ASSERT(pFramesIn != NULL); 53770 53771 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ 53772 53773 /* Clear. */ 53774 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); 53775 53776 /* Accumulate. */ 53777 switch (pConverter->format) 53778 { 53779 case ma_format_u8: 53780 { 53781 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; 53782 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; 53783 53784 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53785 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53786 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53787 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); 53788 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); 53789 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); 53790 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); 53791 } 53792 } 53793 } 53794 } break; 53795 53796 case ma_format_s16: 53797 { 53798 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; 53799 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; 53800 53801 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53802 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53803 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53804 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; 53805 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; 53806 53807 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); 53808 } 53809 } 53810 } 53811 } break; 53812 53813 case ma_format_s24: 53814 { 53815 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; 53816 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; 53817 53818 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53819 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53820 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53821 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); 53822 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); 53823 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); 53824 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); 53825 } 53826 } 53827 } 53828 } break; 53829 53830 case ma_format_s32: 53831 { 53832 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; 53833 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; 53834 53835 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53836 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53837 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53838 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; 53839 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; 53840 53841 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); 53842 } 53843 } 53844 } 53845 } break; 53846 53847 case ma_format_f32: 53848 { 53849 /* */ float* pFramesOutF32 = ( float*)pFramesOut; 53850 const float* pFramesInF32 = (const float*)pFramesIn; 53851 53852 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 53853 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { 53854 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { 53855 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; 53856 } 53857 } 53858 } 53859 } break; 53860 53861 default: return MA_INVALID_OPERATION; /* Unknown format. */ 53862 } 53863 53864 return MA_SUCCESS; 53865 } 53866 53867 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) 53868 { 53869 if (pConverter == NULL) { 53870 return MA_INVALID_ARGS; 53871 } 53872 53873 if (pFramesOut == NULL) { 53874 return MA_INVALID_ARGS; 53875 } 53876 53877 if (pFramesIn == NULL) { 53878 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); 53879 return MA_SUCCESS; 53880 } 53881 53882 switch (pConverter->conversionPath) 53883 { 53884 case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); 53885 case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); 53886 case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); 53887 case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); 53888 case ma_channel_conversion_path_weights: 53889 default: 53890 { 53891 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); 53892 } 53893 } 53894 } 53895 53896 MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) 53897 { 53898 if (pConverter == NULL || pChannelMap == NULL) { 53899 return MA_INVALID_ARGS; 53900 } 53901 53902 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); 53903 53904 return MA_SUCCESS; 53905 } 53906 53907 MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) 53908 { 53909 if (pConverter == NULL || pChannelMap == NULL) { 53910 return MA_INVALID_ARGS; 53911 } 53912 53913 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); 53914 53915 return MA_SUCCESS; 53916 } 53917 53918 53919 /************************************************************************************************************************************************************** 53920 53921 Data Conversion 53922 53923 **************************************************************************************************************************************************************/ 53924 MA_API ma_data_converter_config ma_data_converter_config_init_default(void) 53925 { 53926 ma_data_converter_config config; 53927 MA_ZERO_OBJECT(&config); 53928 53929 config.ditherMode = ma_dither_mode_none; 53930 config.resampling.algorithm = ma_resample_algorithm_linear; 53931 config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ 53932 53933 /* Linear resampling defaults. */ 53934 config.resampling.linear.lpfOrder = 1; 53935 53936 return config; 53937 } 53938 53939 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 53940 { 53941 ma_data_converter_config config = ma_data_converter_config_init_default(); 53942 config.formatIn = formatIn; 53943 config.formatOut = formatOut; 53944 config.channelsIn = channelsIn; 53945 config.channelsOut = channelsOut; 53946 config.sampleRateIn = sampleRateIn; 53947 config.sampleRateOut = sampleRateOut; 53948 53949 return config; 53950 } 53951 53952 53953 typedef struct 53954 { 53955 size_t sizeInBytes; 53956 size_t channelConverterOffset; 53957 size_t resamplerOffset; 53958 } ma_data_converter_heap_layout; 53959 53960 static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) 53961 { 53962 MA_ASSERT(pConfig != NULL); 53963 53964 return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; 53965 } 53966 53967 static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) 53968 { 53969 MA_ASSERT(pConfig != NULL); 53970 53971 /* 53972 We want to avoid as much data conversion as possible. The channel converter and linear 53973 resampler both support s16 and f32 natively. We need to decide on the format to use for this 53974 stage. We call this the mid format because it's used in the middle stage of the conversion 53975 pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it 53976 will do the same thing for the input format. If it's neither we just use f32. If we are using a 53977 custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced 53978 to use that if resampling is required. 53979 */ 53980 if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { 53981 return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ 53982 } else { 53983 /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { 53984 return pConfig->formatOut; 53985 } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { 53986 return pConfig->formatIn; 53987 } else { 53988 return ma_format_f32; 53989 } 53990 } 53991 } 53992 53993 static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) 53994 { 53995 ma_channel_converter_config channelConverterConfig; 53996 53997 MA_ASSERT(pConfig != NULL); 53998 53999 channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); 54000 channelConverterConfig.ppWeights = pConfig->ppChannelWeights; 54001 channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; 54002 54003 return channelConverterConfig; 54004 } 54005 54006 static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) 54007 { 54008 ma_resampler_config resamplerConfig; 54009 ma_uint32 resamplerChannels; 54010 54011 MA_ASSERT(pConfig != NULL); 54012 54013 /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ 54014 if (pConfig->channelsIn < pConfig->channelsOut) { 54015 resamplerChannels = pConfig->channelsIn; 54016 } else { 54017 resamplerChannels = pConfig->channelsOut; 54018 } 54019 54020 resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); 54021 resamplerConfig.linear = pConfig->resampling.linear; 54022 resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; 54023 resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; 54024 54025 return resamplerConfig; 54026 } 54027 54028 static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) 54029 { 54030 ma_result result; 54031 54032 MA_ASSERT(pHeapLayout != NULL); 54033 54034 MA_ZERO_OBJECT(pHeapLayout); 54035 54036 if (pConfig == NULL) { 54037 return MA_INVALID_ARGS; 54038 } 54039 54040 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { 54041 return MA_INVALID_ARGS; 54042 } 54043 54044 pHeapLayout->sizeInBytes = 0; 54045 54046 /* Channel converter. */ 54047 pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; 54048 { 54049 size_t heapSizeInBytes; 54050 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); 54051 54052 result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); 54053 if (result != MA_SUCCESS) { 54054 return result; 54055 } 54056 54057 pHeapLayout->sizeInBytes += heapSizeInBytes; 54058 } 54059 54060 /* Resampler. */ 54061 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; 54062 if (ma_data_converter_config_is_resampler_required(pConfig)) { 54063 size_t heapSizeInBytes; 54064 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); 54065 54066 result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); 54067 if (result != MA_SUCCESS) { 54068 return result; 54069 } 54070 54071 pHeapLayout->sizeInBytes += heapSizeInBytes; 54072 } 54073 54074 /* Make sure allocation size is aligned. */ 54075 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 54076 54077 return MA_SUCCESS; 54078 } 54079 54080 MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) 54081 { 54082 ma_result result; 54083 ma_data_converter_heap_layout heapLayout; 54084 54085 if (pHeapSizeInBytes == NULL) { 54086 return MA_INVALID_ARGS; 54087 } 54088 54089 *pHeapSizeInBytes = 0; 54090 54091 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); 54092 if (result != MA_SUCCESS) { 54093 return result; 54094 } 54095 54096 *pHeapSizeInBytes = heapLayout.sizeInBytes; 54097 54098 return MA_SUCCESS; 54099 } 54100 54101 MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) 54102 { 54103 ma_result result; 54104 ma_data_converter_heap_layout heapLayout; 54105 ma_format midFormat; 54106 ma_bool32 isResamplingRequired; 54107 54108 if (pConverter == NULL) { 54109 return MA_INVALID_ARGS; 54110 } 54111 54112 MA_ZERO_OBJECT(pConverter); 54113 54114 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); 54115 if (result != MA_SUCCESS) { 54116 return result; 54117 } 54118 54119 pConverter->_pHeap = pHeap; 54120 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 54121 54122 pConverter->formatIn = pConfig->formatIn; 54123 pConverter->formatOut = pConfig->formatOut; 54124 pConverter->channelsIn = pConfig->channelsIn; 54125 pConverter->channelsOut = pConfig->channelsOut; 54126 pConverter->sampleRateIn = pConfig->sampleRateIn; 54127 pConverter->sampleRateOut = pConfig->sampleRateOut; 54128 pConverter->ditherMode = pConfig->ditherMode; 54129 54130 /* 54131 Determine if resampling is required. We need to do this so we can determine an appropriate 54132 mid format to use. If resampling is required, the mid format must be ma_format_f32 since 54133 that is the only one that is guaranteed to supported by custom resampling backends. 54134 */ 54135 isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); 54136 midFormat = ma_data_converter_config_get_mid_format(pConfig); 54137 54138 54139 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ 54140 { 54141 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); 54142 54143 result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); 54144 if (result != MA_SUCCESS) { 54145 return result; 54146 } 54147 54148 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ 54149 if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { 54150 pConverter->hasChannelConverter = MA_TRUE; 54151 } 54152 } 54153 54154 54155 /* Resampler. */ 54156 if (isResamplingRequired) { 54157 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); 54158 54159 result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); 54160 if (result != MA_SUCCESS) { 54161 return result; 54162 } 54163 54164 pConverter->hasResampler = MA_TRUE; 54165 } 54166 54167 54168 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ 54169 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { 54170 /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ 54171 if (pConverter->formatIn == pConverter->formatOut) { 54172 /* The formats are the same so we can just pass through. */ 54173 pConverter->hasPreFormatConversion = MA_FALSE; 54174 pConverter->hasPostFormatConversion = MA_FALSE; 54175 } else { 54176 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ 54177 pConverter->hasPreFormatConversion = MA_FALSE; 54178 pConverter->hasPostFormatConversion = MA_TRUE; 54179 } 54180 } else { 54181 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ 54182 if (pConverter->formatIn != midFormat) { 54183 pConverter->hasPreFormatConversion = MA_TRUE; 54184 } 54185 if (pConverter->formatOut != midFormat) { 54186 pConverter->hasPostFormatConversion = MA_TRUE; 54187 } 54188 } 54189 54190 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ 54191 if (pConverter->hasPreFormatConversion == MA_FALSE && 54192 pConverter->hasPostFormatConversion == MA_FALSE && 54193 pConverter->hasChannelConverter == MA_FALSE && 54194 pConverter->hasResampler == MA_FALSE) { 54195 pConverter->isPassthrough = MA_TRUE; 54196 } 54197 54198 54199 /* We now need to determine our execution path. */ 54200 if (pConverter->isPassthrough) { 54201 pConverter->executionPath = ma_data_converter_execution_path_passthrough; 54202 } else { 54203 if (pConverter->channelsIn < pConverter->channelsOut) { 54204 /* Do resampling first, if necessary. */ 54205 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); 54206 54207 if (pConverter->hasResampler) { 54208 pConverter->executionPath = ma_data_converter_execution_path_resample_first; 54209 } else { 54210 pConverter->executionPath = ma_data_converter_execution_path_channels_only; 54211 } 54212 } else { 54213 /* Do channel conversion first, if necessary. */ 54214 if (pConverter->hasChannelConverter) { 54215 if (pConverter->hasResampler) { 54216 pConverter->executionPath = ma_data_converter_execution_path_channels_first; 54217 } else { 54218 pConverter->executionPath = ma_data_converter_execution_path_channels_only; 54219 } 54220 } else { 54221 /* Channel routing not required. */ 54222 if (pConverter->hasResampler) { 54223 pConverter->executionPath = ma_data_converter_execution_path_resample_only; 54224 } else { 54225 pConverter->executionPath = ma_data_converter_execution_path_format_only; 54226 } 54227 } 54228 } 54229 } 54230 54231 return MA_SUCCESS; 54232 } 54233 54234 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) 54235 { 54236 ma_result result; 54237 size_t heapSizeInBytes; 54238 void* pHeap; 54239 54240 result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); 54241 if (result != MA_SUCCESS) { 54242 return result; 54243 } 54244 54245 if (heapSizeInBytes > 0) { 54246 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 54247 if (pHeap == NULL) { 54248 return MA_OUT_OF_MEMORY; 54249 } 54250 } else { 54251 pHeap = NULL; 54252 } 54253 54254 result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); 54255 if (result != MA_SUCCESS) { 54256 ma_free(pHeap, pAllocationCallbacks); 54257 return result; 54258 } 54259 54260 pConverter->_ownsHeap = MA_TRUE; 54261 return MA_SUCCESS; 54262 } 54263 54264 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) 54265 { 54266 if (pConverter == NULL) { 54267 return; 54268 } 54269 54270 if (pConverter->hasResampler) { 54271 ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); 54272 } 54273 54274 ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); 54275 54276 if (pConverter->_ownsHeap) { 54277 ma_free(pConverter->_pHeap, pAllocationCallbacks); 54278 } 54279 } 54280 54281 static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54282 { 54283 ma_uint64 frameCountIn; 54284 ma_uint64 frameCountOut; 54285 ma_uint64 frameCount; 54286 54287 MA_ASSERT(pConverter != NULL); 54288 54289 frameCountIn = 0; 54290 if (pFrameCountIn != NULL) { 54291 frameCountIn = *pFrameCountIn; 54292 } 54293 54294 frameCountOut = 0; 54295 if (pFrameCountOut != NULL) { 54296 frameCountOut = *pFrameCountOut; 54297 } 54298 54299 frameCount = ma_min(frameCountIn, frameCountOut); 54300 54301 if (pFramesOut != NULL) { 54302 if (pFramesIn != NULL) { 54303 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54304 } else { 54305 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54306 } 54307 } 54308 54309 if (pFrameCountIn != NULL) { 54310 *pFrameCountIn = frameCount; 54311 } 54312 if (pFrameCountOut != NULL) { 54313 *pFrameCountOut = frameCount; 54314 } 54315 54316 return MA_SUCCESS; 54317 } 54318 54319 static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54320 { 54321 ma_uint64 frameCountIn; 54322 ma_uint64 frameCountOut; 54323 ma_uint64 frameCount; 54324 54325 MA_ASSERT(pConverter != NULL); 54326 54327 frameCountIn = 0; 54328 if (pFrameCountIn != NULL) { 54329 frameCountIn = *pFrameCountIn; 54330 } 54331 54332 frameCountOut = 0; 54333 if (pFrameCountOut != NULL) { 54334 frameCountOut = *pFrameCountOut; 54335 } 54336 54337 frameCount = ma_min(frameCountIn, frameCountOut); 54338 54339 if (pFramesOut != NULL) { 54340 if (pFramesIn != NULL) { 54341 ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); 54342 } else { 54343 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54344 } 54345 } 54346 54347 if (pFrameCountIn != NULL) { 54348 *pFrameCountIn = frameCount; 54349 } 54350 if (pFrameCountOut != NULL) { 54351 *pFrameCountOut = frameCount; 54352 } 54353 54354 return MA_SUCCESS; 54355 } 54356 54357 54358 static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54359 { 54360 ma_result result = MA_SUCCESS; 54361 ma_uint64 frameCountIn; 54362 ma_uint64 frameCountOut; 54363 ma_uint64 framesProcessedIn; 54364 ma_uint64 framesProcessedOut; 54365 54366 MA_ASSERT(pConverter != NULL); 54367 54368 frameCountIn = 0; 54369 if (pFrameCountIn != NULL) { 54370 frameCountIn = *pFrameCountIn; 54371 } 54372 54373 frameCountOut = 0; 54374 if (pFrameCountOut != NULL) { 54375 frameCountOut = *pFrameCountOut; 54376 } 54377 54378 framesProcessedIn = 0; 54379 framesProcessedOut = 0; 54380 54381 while (framesProcessedOut < frameCountOut) { 54382 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 54383 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); 54384 const void* pFramesInThisIteration; 54385 /* */ void* pFramesOutThisIteration; 54386 ma_uint64 frameCountInThisIteration; 54387 ma_uint64 frameCountOutThisIteration; 54388 54389 if (pFramesIn != NULL) { 54390 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); 54391 } else { 54392 pFramesInThisIteration = NULL; 54393 } 54394 54395 if (pFramesOut != NULL) { 54396 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54397 } else { 54398 pFramesOutThisIteration = NULL; 54399 } 54400 54401 /* Do a pre format conversion if necessary. */ 54402 if (pConverter->hasPreFormatConversion) { 54403 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 54404 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); 54405 54406 frameCountInThisIteration = (frameCountIn - framesProcessedIn); 54407 if (frameCountInThisIteration > tempBufferInCap) { 54408 frameCountInThisIteration = tempBufferInCap; 54409 } 54410 54411 if (pConverter->hasPostFormatConversion) { 54412 if (frameCountInThisIteration > tempBufferOutCap) { 54413 frameCountInThisIteration = tempBufferOutCap; 54414 } 54415 } 54416 54417 if (pFramesInThisIteration != NULL) { 54418 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); 54419 } else { 54420 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); 54421 } 54422 54423 frameCountOutThisIteration = (frameCountOut - framesProcessedOut); 54424 54425 if (pConverter->hasPostFormatConversion) { 54426 /* Both input and output conversion required. Output to the temp buffer. */ 54427 if (frameCountOutThisIteration > tempBufferOutCap) { 54428 frameCountOutThisIteration = tempBufferOutCap; 54429 } 54430 54431 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); 54432 } else { 54433 /* Only pre-format required. Output straight to the output buffer. */ 54434 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); 54435 } 54436 54437 if (result != MA_SUCCESS) { 54438 break; 54439 } 54440 } else { 54441 /* No pre-format required. Just read straight from the input buffer. */ 54442 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); 54443 54444 frameCountInThisIteration = (frameCountIn - framesProcessedIn); 54445 frameCountOutThisIteration = (frameCountOut - framesProcessedOut); 54446 if (frameCountOutThisIteration > tempBufferOutCap) { 54447 frameCountOutThisIteration = tempBufferOutCap; 54448 } 54449 54450 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); 54451 if (result != MA_SUCCESS) { 54452 break; 54453 } 54454 } 54455 54456 /* If we are doing a post format conversion we need to do that now. */ 54457 if (pConverter->hasPostFormatConversion) { 54458 if (pFramesOutThisIteration != NULL) { 54459 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); 54460 } 54461 } 54462 54463 framesProcessedIn += frameCountInThisIteration; 54464 framesProcessedOut += frameCountOutThisIteration; 54465 54466 MA_ASSERT(framesProcessedIn <= frameCountIn); 54467 MA_ASSERT(framesProcessedOut <= frameCountOut); 54468 54469 if (frameCountOutThisIteration == 0) { 54470 break; /* Consumed all of our input data. */ 54471 } 54472 } 54473 54474 if (pFrameCountIn != NULL) { 54475 *pFrameCountIn = framesProcessedIn; 54476 } 54477 if (pFrameCountOut != NULL) { 54478 *pFrameCountOut = framesProcessedOut; 54479 } 54480 54481 return result; 54482 } 54483 54484 static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54485 { 54486 MA_ASSERT(pConverter != NULL); 54487 54488 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { 54489 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ 54490 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54491 } else { 54492 /* Format conversion required. */ 54493 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54494 } 54495 } 54496 54497 static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54498 { 54499 ma_result result; 54500 ma_uint64 frameCountIn; 54501 ma_uint64 frameCountOut; 54502 ma_uint64 frameCount; 54503 54504 MA_ASSERT(pConverter != NULL); 54505 54506 frameCountIn = 0; 54507 if (pFrameCountIn != NULL) { 54508 frameCountIn = *pFrameCountIn; 54509 } 54510 54511 frameCountOut = 0; 54512 if (pFrameCountOut != NULL) { 54513 frameCountOut = *pFrameCountOut; 54514 } 54515 54516 frameCount = ma_min(frameCountIn, frameCountOut); 54517 54518 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { 54519 /* No format conversion required. */ 54520 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); 54521 if (result != MA_SUCCESS) { 54522 return result; 54523 } 54524 } else { 54525 /* Format conversion required. */ 54526 ma_uint64 framesProcessed = 0; 54527 54528 while (framesProcessed < frameCount) { 54529 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 54530 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); 54531 const void* pFramesInThisIteration; 54532 /* */ void* pFramesOutThisIteration; 54533 ma_uint64 frameCountThisIteration; 54534 54535 if (pFramesIn != NULL) { 54536 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); 54537 } else { 54538 pFramesInThisIteration = NULL; 54539 } 54540 54541 if (pFramesOut != NULL) { 54542 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54543 } else { 54544 pFramesOutThisIteration = NULL; 54545 } 54546 54547 /* Do a pre format conversion if necessary. */ 54548 if (pConverter->hasPreFormatConversion) { 54549 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 54550 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); 54551 54552 frameCountThisIteration = (frameCount - framesProcessed); 54553 if (frameCountThisIteration > tempBufferInCap) { 54554 frameCountThisIteration = tempBufferInCap; 54555 } 54556 54557 if (pConverter->hasPostFormatConversion) { 54558 if (frameCountThisIteration > tempBufferOutCap) { 54559 frameCountThisIteration = tempBufferOutCap; 54560 } 54561 } 54562 54563 if (pFramesInThisIteration != NULL) { 54564 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); 54565 } else { 54566 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); 54567 } 54568 54569 if (pConverter->hasPostFormatConversion) { 54570 /* Both input and output conversion required. Output to the temp buffer. */ 54571 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); 54572 } else { 54573 /* Only pre-format required. Output straight to the output buffer. */ 54574 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); 54575 } 54576 54577 if (result != MA_SUCCESS) { 54578 break; 54579 } 54580 } else { 54581 /* No pre-format required. Just read straight from the input buffer. */ 54582 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); 54583 54584 frameCountThisIteration = (frameCount - framesProcessed); 54585 if (frameCountThisIteration > tempBufferOutCap) { 54586 frameCountThisIteration = tempBufferOutCap; 54587 } 54588 54589 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); 54590 if (result != MA_SUCCESS) { 54591 break; 54592 } 54593 } 54594 54595 /* If we are doing a post format conversion we need to do that now. */ 54596 if (pConverter->hasPostFormatConversion) { 54597 if (pFramesOutThisIteration != NULL) { 54598 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); 54599 } 54600 } 54601 54602 framesProcessed += frameCountThisIteration; 54603 } 54604 } 54605 54606 if (pFrameCountIn != NULL) { 54607 *pFrameCountIn = frameCount; 54608 } 54609 if (pFrameCountOut != NULL) { 54610 *pFrameCountOut = frameCount; 54611 } 54612 54613 return MA_SUCCESS; 54614 } 54615 54616 static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54617 { 54618 ma_result result; 54619 ma_uint64 frameCountIn; 54620 ma_uint64 frameCountOut; 54621 ma_uint64 framesProcessedIn; 54622 ma_uint64 framesProcessedOut; 54623 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ 54624 ma_uint64 tempBufferInCap; 54625 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ 54626 ma_uint64 tempBufferMidCap; 54627 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ 54628 ma_uint64 tempBufferOutCap; 54629 54630 MA_ASSERT(pConverter != NULL); 54631 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); 54632 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); 54633 MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); 54634 54635 frameCountIn = 0; 54636 if (pFrameCountIn != NULL) { 54637 frameCountIn = *pFrameCountIn; 54638 } 54639 54640 frameCountOut = 0; 54641 if (pFrameCountOut != NULL) { 54642 frameCountOut = *pFrameCountOut; 54643 } 54644 54645 framesProcessedIn = 0; 54646 framesProcessedOut = 0; 54647 54648 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); 54649 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); 54650 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); 54651 54652 while (framesProcessedOut < frameCountOut) { 54653 ma_uint64 frameCountInThisIteration; 54654 ma_uint64 frameCountOutThisIteration; 54655 const void* pRunningFramesIn = NULL; 54656 void* pRunningFramesOut = NULL; 54657 const void* pResampleBufferIn; 54658 void* pChannelsBufferOut; 54659 54660 if (pFramesIn != NULL) { 54661 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); 54662 } 54663 if (pFramesOut != NULL) { 54664 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54665 } 54666 54667 /* Run input data through the resampler and output it to the temporary buffer. */ 54668 frameCountInThisIteration = (frameCountIn - framesProcessedIn); 54669 54670 if (pConverter->hasPreFormatConversion) { 54671 if (frameCountInThisIteration > tempBufferInCap) { 54672 frameCountInThisIteration = tempBufferInCap; 54673 } 54674 } 54675 54676 frameCountOutThisIteration = (frameCountOut - framesProcessedOut); 54677 if (frameCountOutThisIteration > tempBufferMidCap) { 54678 frameCountOutThisIteration = tempBufferMidCap; 54679 } 54680 54681 /* We can't read more frames than can fit in the output buffer. */ 54682 if (pConverter->hasPostFormatConversion) { 54683 if (frameCountOutThisIteration > tempBufferOutCap) { 54684 frameCountOutThisIteration = tempBufferOutCap; 54685 } 54686 } 54687 54688 /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ 54689 54690 /* 54691 We need to try to predict how many input frames will be required for the resampler. If the 54692 resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further 54693 off we are from this, the more wasted format conversions we'll end up doing. 54694 */ 54695 #if 1 54696 { 54697 ma_uint64 requiredInputFrameCount; 54698 54699 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); 54700 if (result != MA_SUCCESS) { 54701 /* Fall back to a best guess. */ 54702 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; 54703 } 54704 54705 if (frameCountInThisIteration > requiredInputFrameCount) { 54706 frameCountInThisIteration = requiredInputFrameCount; 54707 } 54708 } 54709 #endif 54710 54711 if (pConverter->hasPreFormatConversion) { 54712 if (pFramesIn != NULL) { 54713 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); 54714 pResampleBufferIn = pTempBufferIn; 54715 } else { 54716 pResampleBufferIn = NULL; 54717 } 54718 } else { 54719 pResampleBufferIn = pRunningFramesIn; 54720 } 54721 54722 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); 54723 if (result != MA_SUCCESS) { 54724 return result; 54725 } 54726 54727 54728 /* 54729 The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do 54730 this part if we have an output buffer. 54731 */ 54732 if (pFramesOut != NULL) { 54733 if (pConverter->hasPostFormatConversion) { 54734 pChannelsBufferOut = pTempBufferOut; 54735 } else { 54736 pChannelsBufferOut = pRunningFramesOut; 54737 } 54738 54739 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); 54740 if (result != MA_SUCCESS) { 54741 return result; 54742 } 54743 54744 /* Finally we do post format conversion. */ 54745 if (pConverter->hasPostFormatConversion) { 54746 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); 54747 } 54748 } 54749 54750 54751 framesProcessedIn += frameCountInThisIteration; 54752 framesProcessedOut += frameCountOutThisIteration; 54753 54754 MA_ASSERT(framesProcessedIn <= frameCountIn); 54755 MA_ASSERT(framesProcessedOut <= frameCountOut); 54756 54757 if (frameCountOutThisIteration == 0) { 54758 break; /* Consumed all of our input data. */ 54759 } 54760 } 54761 54762 if (pFrameCountIn != NULL) { 54763 *pFrameCountIn = framesProcessedIn; 54764 } 54765 if (pFrameCountOut != NULL) { 54766 *pFrameCountOut = framesProcessedOut; 54767 } 54768 54769 return MA_SUCCESS; 54770 } 54771 54772 static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54773 { 54774 ma_result result; 54775 ma_uint64 frameCountIn; 54776 ma_uint64 frameCountOut; 54777 ma_uint64 framesProcessedIn; 54778 ma_uint64 framesProcessedOut; 54779 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ 54780 ma_uint64 tempBufferInCap; 54781 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ 54782 ma_uint64 tempBufferMidCap; 54783 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ 54784 ma_uint64 tempBufferOutCap; 54785 54786 MA_ASSERT(pConverter != NULL); 54787 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); 54788 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); 54789 MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); 54790 54791 frameCountIn = 0; 54792 if (pFrameCountIn != NULL) { 54793 frameCountIn = *pFrameCountIn; 54794 } 54795 54796 frameCountOut = 0; 54797 if (pFrameCountOut != NULL) { 54798 frameCountOut = *pFrameCountOut; 54799 } 54800 54801 framesProcessedIn = 0; 54802 framesProcessedOut = 0; 54803 54804 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); 54805 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); 54806 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); 54807 54808 while (framesProcessedOut < frameCountOut) { 54809 ma_uint64 frameCountInThisIteration; 54810 ma_uint64 frameCountOutThisIteration; 54811 const void* pRunningFramesIn = NULL; 54812 void* pRunningFramesOut = NULL; 54813 const void* pChannelsBufferIn; 54814 void* pResampleBufferOut; 54815 54816 if (pFramesIn != NULL) { 54817 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); 54818 } 54819 if (pFramesOut != NULL) { 54820 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); 54821 } 54822 54823 /* 54824 Before doing any processing we need to determine how many frames we should try processing 54825 this iteration, for both input and output. The resampler requires us to perform format and 54826 channel conversion before passing any data into it. If we get our input count wrong, we'll 54827 end up peforming redundant pre-processing. This isn't the end of the world, but it does 54828 result in some inefficiencies proportionate to how far our estimates are off. 54829 54830 If the resampler has a means to calculate exactly how much we'll need, we'll use that. 54831 Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output 54832 frame count first. 54833 */ 54834 frameCountOutThisIteration = (frameCountOut - framesProcessedOut); 54835 if (frameCountOutThisIteration > tempBufferMidCap) { 54836 frameCountOutThisIteration = tempBufferMidCap; 54837 } 54838 54839 if (pConverter->hasPostFormatConversion) { 54840 if (frameCountOutThisIteration > tempBufferOutCap) { 54841 frameCountOutThisIteration = tempBufferOutCap; 54842 } 54843 } 54844 54845 /* Now that we have the output frame count we can determine the input frame count. */ 54846 frameCountInThisIteration = (frameCountIn - framesProcessedIn); 54847 if (pConverter->hasPreFormatConversion) { 54848 if (frameCountInThisIteration > tempBufferInCap) { 54849 frameCountInThisIteration = tempBufferInCap; 54850 } 54851 } 54852 54853 if (frameCountInThisIteration > tempBufferMidCap) { 54854 frameCountInThisIteration = tempBufferMidCap; 54855 } 54856 54857 #if 1 54858 { 54859 ma_uint64 requiredInputFrameCount; 54860 54861 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); 54862 if (result != MA_SUCCESS) { 54863 /* Fall back to a best guess. */ 54864 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; 54865 } 54866 54867 if (frameCountInThisIteration > requiredInputFrameCount) { 54868 frameCountInThisIteration = requiredInputFrameCount; 54869 } 54870 } 54871 #endif 54872 54873 54874 /* Pre format conversion. */ 54875 if (pConverter->hasPreFormatConversion) { 54876 if (pRunningFramesIn != NULL) { 54877 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); 54878 pChannelsBufferIn = pTempBufferIn; 54879 } else { 54880 pChannelsBufferIn = NULL; 54881 } 54882 } else { 54883 pChannelsBufferIn = pRunningFramesIn; 54884 } 54885 54886 54887 /* Channel conversion. */ 54888 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); 54889 if (result != MA_SUCCESS) { 54890 return result; 54891 } 54892 54893 54894 /* Resampling. */ 54895 if (pConverter->hasPostFormatConversion) { 54896 pResampleBufferOut = pTempBufferOut; 54897 } else { 54898 pResampleBufferOut = pRunningFramesOut; 54899 } 54900 54901 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); 54902 if (result != MA_SUCCESS) { 54903 return result; 54904 } 54905 54906 54907 /* Post format conversion. */ 54908 if (pConverter->hasPostFormatConversion) { 54909 if (pRunningFramesOut != NULL) { 54910 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); 54911 } 54912 } 54913 54914 54915 framesProcessedIn += frameCountInThisIteration; 54916 framesProcessedOut += frameCountOutThisIteration; 54917 54918 MA_ASSERT(framesProcessedIn <= frameCountIn); 54919 MA_ASSERT(framesProcessedOut <= frameCountOut); 54920 54921 if (frameCountOutThisIteration == 0) { 54922 break; /* Consumed all of our input data. */ 54923 } 54924 } 54925 54926 if (pFrameCountIn != NULL) { 54927 *pFrameCountIn = framesProcessedIn; 54928 } 54929 if (pFrameCountOut != NULL) { 54930 *pFrameCountOut = framesProcessedOut; 54931 } 54932 54933 return MA_SUCCESS; 54934 } 54935 54936 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) 54937 { 54938 if (pConverter == NULL) { 54939 return MA_INVALID_ARGS; 54940 } 54941 54942 switch (pConverter->executionPath) 54943 { 54944 case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54945 case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54946 case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54947 case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54948 case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54949 case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); 54950 default: return MA_INVALID_OPERATION; /* Should never hit this. */ 54951 } 54952 } 54953 54954 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) 54955 { 54956 if (pConverter == NULL) { 54957 return MA_INVALID_ARGS; 54958 } 54959 54960 if (pConverter->hasResampler == MA_FALSE) { 54961 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ 54962 } 54963 54964 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); 54965 } 54966 54967 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) 54968 { 54969 if (pConverter == NULL) { 54970 return MA_INVALID_ARGS; 54971 } 54972 54973 if (pConverter->hasResampler == MA_FALSE) { 54974 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ 54975 } 54976 54977 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); 54978 } 54979 54980 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) 54981 { 54982 if (pConverter == NULL) { 54983 return 0; 54984 } 54985 54986 if (pConverter->hasResampler) { 54987 return ma_resampler_get_input_latency(&pConverter->resampler); 54988 } 54989 54990 return 0; /* No latency without a resampler. */ 54991 } 54992 54993 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) 54994 { 54995 if (pConverter == NULL) { 54996 return 0; 54997 } 54998 54999 if (pConverter->hasResampler) { 55000 return ma_resampler_get_output_latency(&pConverter->resampler); 55001 } 55002 55003 return 0; /* No latency without a resampler. */ 55004 } 55005 55006 MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) 55007 { 55008 if (pInputFrameCount == NULL) { 55009 return MA_INVALID_ARGS; 55010 } 55011 55012 *pInputFrameCount = 0; 55013 55014 if (pConverter == NULL) { 55015 return MA_INVALID_ARGS; 55016 } 55017 55018 if (pConverter->hasResampler) { 55019 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); 55020 } else { 55021 *pInputFrameCount = outputFrameCount; /* 1:1 */ 55022 return MA_SUCCESS; 55023 } 55024 } 55025 55026 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) 55027 { 55028 if (pOutputFrameCount == NULL) { 55029 return MA_INVALID_ARGS; 55030 } 55031 55032 *pOutputFrameCount = 0; 55033 55034 if (pConverter == NULL) { 55035 return MA_INVALID_ARGS; 55036 } 55037 55038 if (pConverter->hasResampler) { 55039 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); 55040 } else { 55041 *pOutputFrameCount = inputFrameCount; /* 1:1 */ 55042 return MA_SUCCESS; 55043 } 55044 } 55045 55046 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) 55047 { 55048 if (pConverter == NULL || pChannelMap == NULL) { 55049 return MA_INVALID_ARGS; 55050 } 55051 55052 if (pConverter->hasChannelConverter) { 55053 ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); 55054 } else { 55055 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); 55056 } 55057 55058 return MA_SUCCESS; 55059 } 55060 55061 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) 55062 { 55063 if (pConverter == NULL || pChannelMap == NULL) { 55064 return MA_INVALID_ARGS; 55065 } 55066 55067 if (pConverter->hasChannelConverter) { 55068 ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); 55069 } else { 55070 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); 55071 } 55072 55073 return MA_SUCCESS; 55074 } 55075 55076 MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) 55077 { 55078 if (pConverter == NULL) { 55079 return MA_INVALID_ARGS; 55080 } 55081 55082 /* There's nothing to do if we're not resampling. */ 55083 if (pConverter->hasResampler == MA_FALSE) { 55084 return MA_SUCCESS; 55085 } 55086 55087 return ma_resampler_reset(&pConverter->resampler); 55088 } 55089 55090 55091 55092 /************************************************************************************************************************************************************** 55093 55094 Channel Maps 55095 55096 **************************************************************************************************************************************************************/ 55097 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); 55098 55099 MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) 55100 { 55101 if (pChannelMap == NULL) { 55102 return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); 55103 } else { 55104 if (channelIndex >= channelCount) { 55105 return MA_CHANNEL_NONE; 55106 } 55107 55108 return pChannelMap[channelIndex]; 55109 } 55110 } 55111 55112 MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) 55113 { 55114 if (pChannelMap == NULL) { 55115 return; 55116 } 55117 55118 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); 55119 } 55120 55121 55122 static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) 55123 { 55124 if (channelCount == 0 || channelIndex >= channelCount) { 55125 return MA_CHANNEL_NONE; 55126 } 55127 55128 /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ 55129 switch (channelCount) 55130 { 55131 case 0: return MA_CHANNEL_NONE; 55132 55133 case 1: 55134 { 55135 return MA_CHANNEL_MONO; 55136 } break; 55137 55138 case 2: 55139 { 55140 switch (channelIndex) { 55141 case 0: return MA_CHANNEL_FRONT_LEFT; 55142 case 1: return MA_CHANNEL_FRONT_RIGHT; 55143 } 55144 } break; 55145 55146 case 3: /* No defined, but best guess. */ 55147 { 55148 switch (channelIndex) { 55149 case 0: return MA_CHANNEL_FRONT_LEFT; 55150 case 1: return MA_CHANNEL_FRONT_RIGHT; 55151 case 2: return MA_CHANNEL_FRONT_CENTER; 55152 } 55153 } break; 55154 55155 case 4: 55156 { 55157 switch (channelIndex) { 55158 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP 55159 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ 55160 case 0: return MA_CHANNEL_FRONT_LEFT; 55161 case 1: return MA_CHANNEL_FRONT_RIGHT; 55162 case 2: return MA_CHANNEL_FRONT_CENTER; 55163 case 3: return MA_CHANNEL_BACK_CENTER; 55164 #else 55165 /* Quad. */ 55166 case 0: return MA_CHANNEL_FRONT_LEFT; 55167 case 1: return MA_CHANNEL_FRONT_RIGHT; 55168 case 2: return MA_CHANNEL_BACK_LEFT; 55169 case 3: return MA_CHANNEL_BACK_RIGHT; 55170 #endif 55171 } 55172 } break; 55173 55174 case 5: /* Not defined, but best guess. */ 55175 { 55176 switch (channelIndex) { 55177 case 0: return MA_CHANNEL_FRONT_LEFT; 55178 case 1: return MA_CHANNEL_FRONT_RIGHT; 55179 case 2: return MA_CHANNEL_FRONT_CENTER; 55180 case 3: return MA_CHANNEL_BACK_LEFT; 55181 case 4: return MA_CHANNEL_BACK_RIGHT; 55182 } 55183 } break; 55184 55185 case 6: 55186 { 55187 switch (channelIndex) { 55188 case 0: return MA_CHANNEL_FRONT_LEFT; 55189 case 1: return MA_CHANNEL_FRONT_RIGHT; 55190 case 2: return MA_CHANNEL_FRONT_CENTER; 55191 case 3: return MA_CHANNEL_LFE; 55192 case 4: return MA_CHANNEL_SIDE_LEFT; 55193 case 5: return MA_CHANNEL_SIDE_RIGHT; 55194 } 55195 } break; 55196 55197 case 7: /* Not defined, but best guess. */ 55198 { 55199 switch (channelIndex) { 55200 case 0: return MA_CHANNEL_FRONT_LEFT; 55201 case 1: return MA_CHANNEL_FRONT_RIGHT; 55202 case 2: return MA_CHANNEL_FRONT_CENTER; 55203 case 3: return MA_CHANNEL_LFE; 55204 case 4: return MA_CHANNEL_BACK_CENTER; 55205 case 5: return MA_CHANNEL_SIDE_LEFT; 55206 case 6: return MA_CHANNEL_SIDE_RIGHT; 55207 } 55208 } break; 55209 55210 case 8: 55211 default: 55212 { 55213 switch (channelIndex) { 55214 case 0: return MA_CHANNEL_FRONT_LEFT; 55215 case 1: return MA_CHANNEL_FRONT_RIGHT; 55216 case 2: return MA_CHANNEL_FRONT_CENTER; 55217 case 3: return MA_CHANNEL_LFE; 55218 case 4: return MA_CHANNEL_BACK_LEFT; 55219 case 5: return MA_CHANNEL_BACK_RIGHT; 55220 case 6: return MA_CHANNEL_SIDE_LEFT; 55221 case 7: return MA_CHANNEL_SIDE_RIGHT; 55222 } 55223 } break; 55224 } 55225 55226 if (channelCount > 8) { 55227 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55228 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); 55229 } 55230 } 55231 55232 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55233 return MA_CHANNEL_NONE; 55234 } 55235 55236 static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) 55237 { 55238 switch (channelCount) 55239 { 55240 case 0: return MA_CHANNEL_NONE; 55241 55242 case 1: 55243 { 55244 return MA_CHANNEL_MONO; 55245 } break; 55246 55247 case 2: 55248 { 55249 switch (channelIndex) { 55250 case 0: return MA_CHANNEL_FRONT_LEFT; 55251 case 1: return MA_CHANNEL_FRONT_RIGHT; 55252 } 55253 } break; 55254 55255 case 3: 55256 { 55257 switch (channelIndex) { 55258 case 0: return MA_CHANNEL_FRONT_LEFT; 55259 case 1: return MA_CHANNEL_FRONT_RIGHT; 55260 case 2: return MA_CHANNEL_FRONT_CENTER; 55261 } 55262 } break; 55263 55264 case 4: 55265 { 55266 switch (channelIndex) { 55267 case 0: return MA_CHANNEL_FRONT_LEFT; 55268 case 1: return MA_CHANNEL_FRONT_RIGHT; 55269 case 2: return MA_CHANNEL_BACK_LEFT; 55270 case 3: return MA_CHANNEL_BACK_RIGHT; 55271 } 55272 } break; 55273 55274 case 5: 55275 { 55276 switch (channelIndex) { 55277 case 0: return MA_CHANNEL_FRONT_LEFT; 55278 case 1: return MA_CHANNEL_FRONT_RIGHT; 55279 case 2: return MA_CHANNEL_BACK_LEFT; 55280 case 3: return MA_CHANNEL_BACK_RIGHT; 55281 case 4: return MA_CHANNEL_FRONT_CENTER; 55282 } 55283 } break; 55284 55285 case 6: 55286 { 55287 switch (channelIndex) { 55288 case 0: return MA_CHANNEL_FRONT_LEFT; 55289 case 1: return MA_CHANNEL_FRONT_RIGHT; 55290 case 2: return MA_CHANNEL_BACK_LEFT; 55291 case 3: return MA_CHANNEL_BACK_RIGHT; 55292 case 4: return MA_CHANNEL_FRONT_CENTER; 55293 case 5: return MA_CHANNEL_LFE; 55294 } 55295 } break; 55296 55297 case 7: 55298 { 55299 switch (channelIndex) { 55300 case 0: return MA_CHANNEL_FRONT_LEFT; 55301 case 1: return MA_CHANNEL_FRONT_RIGHT; 55302 case 2: return MA_CHANNEL_BACK_LEFT; 55303 case 3: return MA_CHANNEL_BACK_RIGHT; 55304 case 4: return MA_CHANNEL_FRONT_CENTER; 55305 case 5: return MA_CHANNEL_LFE; 55306 case 6: return MA_CHANNEL_BACK_CENTER; 55307 } 55308 } break; 55309 55310 case 8: 55311 default: 55312 { 55313 switch (channelIndex) { 55314 case 0: return MA_CHANNEL_FRONT_LEFT; 55315 case 1: return MA_CHANNEL_FRONT_RIGHT; 55316 case 2: return MA_CHANNEL_BACK_LEFT; 55317 case 3: return MA_CHANNEL_BACK_RIGHT; 55318 case 4: return MA_CHANNEL_FRONT_CENTER; 55319 case 5: return MA_CHANNEL_LFE; 55320 case 6: return MA_CHANNEL_SIDE_LEFT; 55321 case 7: return MA_CHANNEL_SIDE_RIGHT; 55322 } 55323 } break; 55324 } 55325 55326 if (channelCount > 8) { 55327 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55328 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); 55329 } 55330 } 55331 55332 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55333 return MA_CHANNEL_NONE; 55334 } 55335 55336 static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) 55337 { 55338 switch (channelCount) 55339 { 55340 case 0: return MA_CHANNEL_NONE; 55341 55342 case 1: 55343 { 55344 return MA_CHANNEL_MONO; 55345 } break; 55346 55347 case 2: 55348 { 55349 switch (channelIndex) { 55350 case 0: return MA_CHANNEL_FRONT_LEFT; 55351 case 1: return MA_CHANNEL_FRONT_RIGHT; 55352 } 55353 } break; 55354 55355 case 3: 55356 { 55357 switch (channelIndex) { 55358 case 0: return MA_CHANNEL_FRONT_LEFT; 55359 case 1: return MA_CHANNEL_FRONT_RIGHT; 55360 case 2: return MA_CHANNEL_FRONT_CENTER; 55361 } 55362 } break; 55363 55364 case 4: 55365 { 55366 switch (channelIndex) { 55367 case 0: return MA_CHANNEL_FRONT_LEFT; 55368 case 2: return MA_CHANNEL_FRONT_CENTER; 55369 case 1: return MA_CHANNEL_FRONT_RIGHT; 55370 case 3: return MA_CHANNEL_BACK_CENTER; 55371 } 55372 } break; 55373 55374 case 5: 55375 { 55376 switch (channelIndex) { 55377 case 0: return MA_CHANNEL_FRONT_LEFT; 55378 case 1: return MA_CHANNEL_FRONT_RIGHT; 55379 case 2: return MA_CHANNEL_FRONT_CENTER; 55380 case 3: return MA_CHANNEL_BACK_LEFT; 55381 case 4: return MA_CHANNEL_BACK_RIGHT; 55382 } 55383 } break; 55384 55385 case 6: 55386 default: 55387 { 55388 switch (channelIndex) { 55389 case 0: return MA_CHANNEL_FRONT_LEFT; 55390 case 1: return MA_CHANNEL_SIDE_LEFT; 55391 case 2: return MA_CHANNEL_FRONT_CENTER; 55392 case 3: return MA_CHANNEL_FRONT_RIGHT; 55393 case 4: return MA_CHANNEL_SIDE_RIGHT; 55394 case 5: return MA_CHANNEL_BACK_CENTER; 55395 } 55396 } break; 55397 } 55398 55399 if (channelCount > 6) { 55400 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55401 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); 55402 } 55403 } 55404 55405 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55406 return MA_CHANNEL_NONE; 55407 } 55408 55409 static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) 55410 { 55411 switch (channelCount) 55412 { 55413 case 0: return MA_CHANNEL_NONE; 55414 55415 case 1: 55416 { 55417 return MA_CHANNEL_MONO; 55418 } break; 55419 55420 case 2: 55421 { 55422 switch (channelIndex) { 55423 case 0: return MA_CHANNEL_FRONT_LEFT; 55424 case 1: return MA_CHANNEL_FRONT_RIGHT; 55425 } 55426 } break; 55427 55428 case 3: 55429 { 55430 switch (channelIndex) { 55431 case 0: return MA_CHANNEL_FRONT_LEFT; 55432 case 1: return MA_CHANNEL_FRONT_RIGHT; 55433 case 2: return MA_CHANNEL_FRONT_CENTER; 55434 } 55435 } break; 55436 55437 case 4: 55438 { 55439 switch (channelIndex) { 55440 case 0: return MA_CHANNEL_FRONT_LEFT; 55441 case 1: return MA_CHANNEL_FRONT_RIGHT; 55442 case 2: return MA_CHANNEL_BACK_LEFT; 55443 case 3: return MA_CHANNEL_BACK_RIGHT; 55444 } 55445 } break; 55446 55447 case 5: 55448 { 55449 switch (channelIndex) { 55450 case 0: return MA_CHANNEL_FRONT_LEFT; 55451 case 1: return MA_CHANNEL_FRONT_RIGHT; 55452 case 2: return MA_CHANNEL_FRONT_CENTER; 55453 case 3: return MA_CHANNEL_BACK_LEFT; 55454 case 4: return MA_CHANNEL_BACK_RIGHT; 55455 } 55456 } break; 55457 55458 case 6: 55459 { 55460 switch (channelIndex) { 55461 case 0: return MA_CHANNEL_FRONT_LEFT; 55462 case 1: return MA_CHANNEL_FRONT_RIGHT; 55463 case 2: return MA_CHANNEL_FRONT_CENTER; 55464 case 3: return MA_CHANNEL_LFE; 55465 case 4: return MA_CHANNEL_BACK_LEFT; 55466 case 5: return MA_CHANNEL_BACK_RIGHT; 55467 } 55468 } break; 55469 55470 case 7: 55471 { 55472 switch (channelIndex) { 55473 case 0: return MA_CHANNEL_FRONT_LEFT; 55474 case 1: return MA_CHANNEL_FRONT_RIGHT; 55475 case 2: return MA_CHANNEL_FRONT_CENTER; 55476 case 3: return MA_CHANNEL_LFE; 55477 case 4: return MA_CHANNEL_BACK_CENTER; 55478 case 5: return MA_CHANNEL_SIDE_LEFT; 55479 case 6: return MA_CHANNEL_SIDE_RIGHT; 55480 } 55481 } break; 55482 55483 case 8: 55484 default: 55485 { 55486 switch (channelIndex) { 55487 case 0: return MA_CHANNEL_FRONT_LEFT; 55488 case 1: return MA_CHANNEL_FRONT_RIGHT; 55489 case 2: return MA_CHANNEL_FRONT_CENTER; 55490 case 3: return MA_CHANNEL_LFE; 55491 case 4: return MA_CHANNEL_BACK_LEFT; 55492 case 5: return MA_CHANNEL_BACK_RIGHT; 55493 case 6: return MA_CHANNEL_SIDE_LEFT; 55494 case 7: return MA_CHANNEL_SIDE_RIGHT; 55495 } 55496 } break; 55497 } 55498 55499 if (channelCount > 8) { 55500 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55501 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); 55502 } 55503 } 55504 55505 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55506 return MA_CHANNEL_NONE; 55507 } 55508 55509 static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) 55510 { 55511 switch (channelCount) 55512 { 55513 case 0: return MA_CHANNEL_NONE; 55514 55515 case 1: 55516 { 55517 return MA_CHANNEL_MONO; 55518 } break; 55519 55520 case 2: 55521 { 55522 switch (channelIndex) { 55523 case 0: return MA_CHANNEL_FRONT_LEFT; 55524 case 1: return MA_CHANNEL_FRONT_RIGHT; 55525 } 55526 } break; 55527 55528 case 3: 55529 { 55530 switch (channelIndex) { 55531 case 0: return MA_CHANNEL_FRONT_LEFT; 55532 case 1: return MA_CHANNEL_FRONT_CENTER; 55533 case 2: return MA_CHANNEL_FRONT_RIGHT; 55534 } 55535 } break; 55536 55537 case 4: 55538 { 55539 switch (channelIndex) { 55540 case 0: return MA_CHANNEL_FRONT_LEFT; 55541 case 1: return MA_CHANNEL_FRONT_RIGHT; 55542 case 2: return MA_CHANNEL_BACK_LEFT; 55543 case 3: return MA_CHANNEL_BACK_RIGHT; 55544 } 55545 } break; 55546 55547 case 5: 55548 { 55549 switch (channelIndex) { 55550 case 0: return MA_CHANNEL_FRONT_LEFT; 55551 case 1: return MA_CHANNEL_FRONT_CENTER; 55552 case 2: return MA_CHANNEL_FRONT_RIGHT; 55553 case 3: return MA_CHANNEL_BACK_LEFT; 55554 case 4: return MA_CHANNEL_BACK_RIGHT; 55555 } 55556 } break; 55557 55558 case 6: 55559 { 55560 switch (channelIndex) { 55561 case 0: return MA_CHANNEL_FRONT_LEFT; 55562 case 1: return MA_CHANNEL_FRONT_CENTER; 55563 case 2: return MA_CHANNEL_FRONT_RIGHT; 55564 case 3: return MA_CHANNEL_BACK_LEFT; 55565 case 4: return MA_CHANNEL_BACK_RIGHT; 55566 case 5: return MA_CHANNEL_LFE; 55567 } 55568 } break; 55569 55570 case 7: 55571 { 55572 switch (channelIndex) { 55573 case 0: return MA_CHANNEL_FRONT_LEFT; 55574 case 1: return MA_CHANNEL_FRONT_CENTER; 55575 case 2: return MA_CHANNEL_FRONT_RIGHT; 55576 case 3: return MA_CHANNEL_SIDE_LEFT; 55577 case 4: return MA_CHANNEL_SIDE_RIGHT; 55578 case 5: return MA_CHANNEL_BACK_CENTER; 55579 case 6: return MA_CHANNEL_LFE; 55580 } 55581 } break; 55582 55583 case 8: 55584 default: 55585 { 55586 switch (channelIndex) { 55587 case 0: return MA_CHANNEL_FRONT_LEFT; 55588 case 1: return MA_CHANNEL_FRONT_CENTER; 55589 case 2: return MA_CHANNEL_FRONT_RIGHT; 55590 case 3: return MA_CHANNEL_SIDE_LEFT; 55591 case 4: return MA_CHANNEL_SIDE_RIGHT; 55592 case 5: return MA_CHANNEL_BACK_LEFT; 55593 case 6: return MA_CHANNEL_BACK_RIGHT; 55594 case 7: return MA_CHANNEL_LFE; 55595 } 55596 } break; 55597 } 55598 55599 if (channelCount > 8) { 55600 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55601 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); 55602 } 55603 } 55604 55605 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55606 return MA_CHANNEL_NONE; 55607 } 55608 55609 static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) 55610 { 55611 switch (channelCount) 55612 { 55613 case 0: return MA_CHANNEL_NONE; 55614 55615 case 1: 55616 { 55617 return MA_CHANNEL_MONO; 55618 } break; 55619 55620 case 2: 55621 { 55622 switch (channelIndex) { 55623 case 0: return MA_CHANNEL_FRONT_LEFT; 55624 case 1: return MA_CHANNEL_FRONT_RIGHT; 55625 } 55626 } break; 55627 55628 case 3: 55629 { 55630 switch (channelIndex) { 55631 case 0: return MA_CHANNEL_FRONT_LEFT; 55632 case 1: return MA_CHANNEL_FRONT_RIGHT; 55633 case 2: return MA_CHANNEL_FRONT_CENTER; 55634 } 55635 } break; 55636 55637 case 4: 55638 { 55639 switch (channelIndex) { 55640 case 0: return MA_CHANNEL_FRONT_LEFT; 55641 case 1: return MA_CHANNEL_FRONT_RIGHT; 55642 case 2: return MA_CHANNEL_BACK_LEFT; 55643 case 3: return MA_CHANNEL_BACK_RIGHT; 55644 } 55645 } break; 55646 55647 case 5: 55648 { 55649 switch (channelIndex) { 55650 case 0: return MA_CHANNEL_FRONT_LEFT; 55651 case 1: return MA_CHANNEL_FRONT_RIGHT; 55652 case 2: return MA_CHANNEL_FRONT_CENTER; 55653 case 3: return MA_CHANNEL_BACK_LEFT; 55654 case 4: return MA_CHANNEL_BACK_RIGHT; 55655 } 55656 } break; 55657 55658 case 6: 55659 { 55660 switch (channelIndex) { 55661 case 0: return MA_CHANNEL_FRONT_LEFT; 55662 case 1: return MA_CHANNEL_FRONT_CENTER; 55663 case 2: return MA_CHANNEL_FRONT_RIGHT; 55664 case 3: return MA_CHANNEL_BACK_LEFT; 55665 case 4: return MA_CHANNEL_BACK_RIGHT; 55666 case 5: return MA_CHANNEL_LFE; 55667 } 55668 } break; 55669 55670 case 7: 55671 { 55672 switch (channelIndex) { 55673 case 0: return MA_CHANNEL_FRONT_LEFT; 55674 case 1: return MA_CHANNEL_FRONT_CENTER; 55675 case 2: return MA_CHANNEL_FRONT_RIGHT; 55676 case 3: return MA_CHANNEL_SIDE_LEFT; 55677 case 4: return MA_CHANNEL_SIDE_RIGHT; 55678 case 5: return MA_CHANNEL_BACK_CENTER; 55679 case 6: return MA_CHANNEL_LFE; 55680 } 55681 } break; 55682 55683 case 8: 55684 default: 55685 { 55686 switch (channelIndex) { 55687 case 0: return MA_CHANNEL_FRONT_LEFT; 55688 case 1: return MA_CHANNEL_FRONT_CENTER; 55689 case 2: return MA_CHANNEL_FRONT_RIGHT; 55690 case 3: return MA_CHANNEL_SIDE_LEFT; 55691 case 4: return MA_CHANNEL_SIDE_RIGHT; 55692 case 5: return MA_CHANNEL_BACK_LEFT; 55693 case 6: return MA_CHANNEL_BACK_RIGHT; 55694 case 7: return MA_CHANNEL_LFE; 55695 } 55696 } break; 55697 } 55698 55699 if (channelCount > 8) { 55700 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55701 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); 55702 } 55703 } 55704 55705 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55706 return MA_CHANNEL_NONE; 55707 } 55708 55709 static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) 55710 { 55711 switch (channelCount) 55712 { 55713 case 0: return MA_CHANNEL_NONE; 55714 55715 case 1: 55716 { 55717 return MA_CHANNEL_MONO; 55718 } break; 55719 55720 case 2: 55721 { 55722 switch (channelIndex) { 55723 case 0: return MA_CHANNEL_FRONT_LEFT; 55724 case 1: return MA_CHANNEL_FRONT_RIGHT; 55725 } 55726 } break; 55727 55728 case 3: /* No defined, but best guess. */ 55729 { 55730 switch (channelIndex) { 55731 case 0: return MA_CHANNEL_FRONT_LEFT; 55732 case 1: return MA_CHANNEL_FRONT_RIGHT; 55733 case 2: return MA_CHANNEL_FRONT_CENTER; 55734 } 55735 } break; 55736 55737 case 4: 55738 { 55739 switch (channelIndex) { 55740 case 0: return MA_CHANNEL_FRONT_LEFT; 55741 case 1: return MA_CHANNEL_FRONT_RIGHT; 55742 case 2: return MA_CHANNEL_BACK_LEFT; 55743 case 3: return MA_CHANNEL_BACK_RIGHT; 55744 } 55745 } break; 55746 55747 case 5: /* Not defined, but best guess. */ 55748 { 55749 switch (channelIndex) { 55750 case 0: return MA_CHANNEL_FRONT_LEFT; 55751 case 1: return MA_CHANNEL_FRONT_RIGHT; 55752 case 2: return MA_CHANNEL_BACK_LEFT; 55753 case 3: return MA_CHANNEL_BACK_RIGHT; 55754 case 4: return MA_CHANNEL_FRONT_CENTER; 55755 } 55756 } break; 55757 55758 case 6: 55759 default: 55760 { 55761 switch (channelIndex) { 55762 case 0: return MA_CHANNEL_FRONT_LEFT; 55763 case 1: return MA_CHANNEL_FRONT_RIGHT; 55764 case 2: return MA_CHANNEL_BACK_LEFT; 55765 case 3: return MA_CHANNEL_BACK_RIGHT; 55766 case 4: return MA_CHANNEL_FRONT_CENTER; 55767 case 5: return MA_CHANNEL_LFE; 55768 } 55769 } break; 55770 } 55771 55772 if (channelCount > 6) { 55773 if (channelIndex < 32) { /* We have 32 AUX channels. */ 55774 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); 55775 } 55776 } 55777 55778 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ 55779 return MA_CHANNEL_NONE; 55780 } 55781 55782 55783 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) 55784 { 55785 if (channelCount == 0 || channelIndex >= channelCount) { 55786 return MA_CHANNEL_NONE; 55787 } 55788 55789 switch (standardChannelMap) 55790 { 55791 case ma_standard_channel_map_alsa: 55792 { 55793 return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); 55794 } break; 55795 55796 case ma_standard_channel_map_rfc3551: 55797 { 55798 return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); 55799 } break; 55800 55801 case ma_standard_channel_map_flac: 55802 { 55803 return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); 55804 } break; 55805 55806 case ma_standard_channel_map_vorbis: 55807 { 55808 return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); 55809 } break; 55810 55811 case ma_standard_channel_map_sound4: 55812 { 55813 return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); 55814 } break; 55815 55816 case ma_standard_channel_map_sndio: 55817 { 55818 return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); 55819 } break; 55820 55821 case ma_standard_channel_map_microsoft: /* Also default. */ 55822 /*case ma_standard_channel_map_default;*/ 55823 default: 55824 { 55825 return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); 55826 } break; 55827 } 55828 } 55829 55830 MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) 55831 { 55832 ma_uint32 iChannel; 55833 55834 if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { 55835 return; 55836 } 55837 55838 for (iChannel = 0; iChannel < channels; iChannel += 1) { 55839 if (channelMapCap == 0) { 55840 break; /* Ran out of room. */ 55841 } 55842 55843 pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); 55844 pChannelMap += 1; 55845 channelMapCap -= 1; 55846 } 55847 } 55848 55849 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) 55850 { 55851 if (pOut != NULL && pIn != NULL && channels > 0) { 55852 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); 55853 } 55854 } 55855 55856 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) 55857 { 55858 if (pOut == NULL || channels == 0) { 55859 return; 55860 } 55861 55862 if (pIn != NULL) { 55863 ma_channel_map_copy(pOut, pIn, channels); 55864 } else { 55865 ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); 55866 } 55867 } 55868 55869 MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) 55870 { 55871 /* A channel count of 0 is invalid. */ 55872 if (channels == 0) { 55873 return MA_FALSE; 55874 } 55875 55876 /* It does not make sense to have a mono channel when there is more than 1 channel. */ 55877 if (channels > 1) { 55878 ma_uint32 iChannel; 55879 for (iChannel = 0; iChannel < channels; ++iChannel) { 55880 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { 55881 return MA_FALSE; 55882 } 55883 } 55884 } 55885 55886 return MA_TRUE; 55887 } 55888 55889 MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) 55890 { 55891 ma_uint32 iChannel; 55892 55893 if (pChannelMapA == pChannelMapB) { 55894 return MA_TRUE; 55895 } 55896 55897 for (iChannel = 0; iChannel < channels; ++iChannel) { 55898 if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { 55899 return MA_FALSE; 55900 } 55901 } 55902 55903 return MA_TRUE; 55904 } 55905 55906 MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) 55907 { 55908 ma_uint32 iChannel; 55909 55910 /* A null channel map is equivalent to the default channel map. */ 55911 if (pChannelMap == NULL) { 55912 return MA_FALSE; 55913 } 55914 55915 for (iChannel = 0; iChannel < channels; ++iChannel) { 55916 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { 55917 return MA_FALSE; 55918 } 55919 } 55920 55921 return MA_TRUE; 55922 } 55923 55924 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) 55925 { 55926 return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); 55927 } 55928 55929 MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) 55930 { 55931 ma_uint32 iChannel; 55932 55933 if (pChannelIndex != NULL) { 55934 *pChannelIndex = (ma_uint32)-1; 55935 } 55936 55937 for (iChannel = 0; iChannel < channels; ++iChannel) { 55938 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { 55939 if (pChannelIndex != NULL) { 55940 *pChannelIndex = iChannel; 55941 } 55942 55943 return MA_TRUE; 55944 } 55945 } 55946 55947 /* Getting here means the channel position was not found. */ 55948 return MA_FALSE; 55949 } 55950 55951 MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) 55952 { 55953 size_t len; 55954 ma_uint32 iChannel; 55955 55956 len = 0; 55957 55958 for (iChannel = 0; iChannel < channels; iChannel += 1) { 55959 const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); 55960 size_t channelStrLen = strlen(pChannelStr); 55961 55962 /* Append the string if necessary. */ 55963 if (pBufferOut != NULL && bufferCap > len + channelStrLen) { 55964 MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); 55965 } 55966 len += channelStrLen; 55967 55968 /* Append a space if it's not the last item. */ 55969 if (iChannel+1 < channels) { 55970 if (pBufferOut != NULL && bufferCap > len + 1) { 55971 pBufferOut[len] = ' '; 55972 } 55973 len += 1; 55974 } 55975 } 55976 55977 /* Null terminate. Don't increment the length here. */ 55978 if (pBufferOut != NULL && bufferCap > len + 1) { 55979 pBufferOut[len] = '\0'; 55980 } 55981 55982 return len; 55983 } 55984 55985 MA_API const char* ma_channel_position_to_string(ma_channel channel) 55986 { 55987 switch (channel) 55988 { 55989 case MA_CHANNEL_NONE : return "CHANNEL_NONE"; 55990 case MA_CHANNEL_MONO : return "CHANNEL_MONO"; 55991 case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; 55992 case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; 55993 case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; 55994 case MA_CHANNEL_LFE : return "CHANNEL_LFE"; 55995 case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; 55996 case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; 55997 case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; 55998 case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; 55999 case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; 56000 case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; 56001 case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; 56002 case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; 56003 case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; 56004 case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; 56005 case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; 56006 case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; 56007 case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; 56008 case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; 56009 case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; 56010 case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; 56011 case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; 56012 case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; 56013 case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; 56014 case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; 56015 case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; 56016 case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; 56017 case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; 56018 case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; 56019 case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; 56020 case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; 56021 case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; 56022 case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; 56023 case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; 56024 case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; 56025 case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; 56026 case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; 56027 case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; 56028 case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; 56029 case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; 56030 case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; 56031 case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; 56032 case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; 56033 case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; 56034 case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; 56035 case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; 56036 case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; 56037 case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; 56038 case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; 56039 case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; 56040 case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; 56041 default: break; 56042 } 56043 56044 return "UNKNOWN"; 56045 } 56046 56047 56048 56049 /************************************************************************************************************************************************************** 56050 56051 Conversion Helpers 56052 56053 **************************************************************************************************************************************************************/ 56054 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) 56055 { 56056 ma_data_converter_config config; 56057 56058 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); 56059 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); 56060 56061 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); 56062 } 56063 56064 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) 56065 { 56066 ma_result result; 56067 ma_data_converter converter; 56068 56069 if (frameCountIn == 0 || pConfig == NULL) { 56070 return 0; 56071 } 56072 56073 result = ma_data_converter_init(pConfig, NULL, &converter); 56074 if (result != MA_SUCCESS) { 56075 return 0; /* Failed to initialize the data converter. */ 56076 } 56077 56078 if (pOut == NULL) { 56079 result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); 56080 if (result != MA_SUCCESS) { 56081 if (result == MA_NOT_IMPLEMENTED) { 56082 /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ 56083 frameCountOut = 0; 56084 56085 while (frameCountIn > 0) { 56086 ma_uint64 framesProcessedIn = frameCountIn; 56087 ma_uint64 framesProcessedOut = 0xFFFFFFFF; 56088 56089 result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); 56090 if (result != MA_SUCCESS) { 56091 break; 56092 } 56093 56094 frameCountIn -= framesProcessedIn; 56095 } 56096 } 56097 } 56098 } else { 56099 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); 56100 if (result != MA_SUCCESS) { 56101 frameCountOut = 0; 56102 } 56103 } 56104 56105 ma_data_converter_uninit(&converter, NULL); 56106 return frameCountOut; 56107 } 56108 56109 56110 /************************************************************************************************************************************************************** 56111 56112 Ring Buffer 56113 56114 **************************************************************************************************************************************************************/ 56115 static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) 56116 { 56117 return encodedOffset & 0x7FFFFFFF; 56118 } 56119 56120 static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) 56121 { 56122 return encodedOffset & 0x80000000; 56123 } 56124 56125 static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) 56126 { 56127 MA_ASSERT(pRB != NULL); 56128 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); 56129 } 56130 56131 static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) 56132 { 56133 MA_ASSERT(pRB != NULL); 56134 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); 56135 } 56136 56137 static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) 56138 { 56139 return offsetLoopFlag | offsetInBytes; 56140 } 56141 56142 static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) 56143 { 56144 MA_ASSERT(pOffsetInBytes != NULL); 56145 MA_ASSERT(pOffsetLoopFlag != NULL); 56146 56147 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); 56148 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); 56149 } 56150 56151 56152 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) 56153 { 56154 ma_result result; 56155 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); 56156 56157 if (pRB == NULL) { 56158 return MA_INVALID_ARGS; 56159 } 56160 56161 if (subbufferSizeInBytes == 0 || subbufferCount == 0) { 56162 return MA_INVALID_ARGS; 56163 } 56164 56165 if (subbufferSizeInBytes > maxSubBufferSize) { 56166 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ 56167 } 56168 56169 56170 MA_ZERO_OBJECT(pRB); 56171 56172 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); 56173 if (result != MA_SUCCESS) { 56174 return result; 56175 } 56176 56177 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; 56178 pRB->subbufferCount = (ma_uint32)subbufferCount; 56179 56180 if (pOptionalPreallocatedBuffer != NULL) { 56181 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; 56182 pRB->pBuffer = pOptionalPreallocatedBuffer; 56183 } else { 56184 size_t bufferSizeInBytes; 56185 56186 /* 56187 Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this 56188 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. 56189 */ 56190 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; 56191 56192 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; 56193 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); 56194 if (pRB->pBuffer == NULL) { 56195 return MA_OUT_OF_MEMORY; 56196 } 56197 56198 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); 56199 pRB->ownsBuffer = MA_TRUE; 56200 } 56201 56202 return MA_SUCCESS; 56203 } 56204 56205 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) 56206 { 56207 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); 56208 } 56209 56210 MA_API void ma_rb_uninit(ma_rb* pRB) 56211 { 56212 if (pRB == NULL) { 56213 return; 56214 } 56215 56216 if (pRB->ownsBuffer) { 56217 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); 56218 } 56219 } 56220 56221 MA_API void ma_rb_reset(ma_rb* pRB) 56222 { 56223 if (pRB == NULL) { 56224 return; 56225 } 56226 56227 ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); 56228 ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); 56229 } 56230 56231 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) 56232 { 56233 ma_uint32 writeOffset; 56234 ma_uint32 writeOffsetInBytes; 56235 ma_uint32 writeOffsetLoopFlag; 56236 ma_uint32 readOffset; 56237 ma_uint32 readOffsetInBytes; 56238 ma_uint32 readOffsetLoopFlag; 56239 size_t bytesAvailable; 56240 size_t bytesRequested; 56241 56242 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { 56243 return MA_INVALID_ARGS; 56244 } 56245 56246 /* The returned buffer should never move ahead of the write pointer. */ 56247 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56248 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56249 56250 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56251 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56252 56253 /* 56254 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we 56255 can only read up to the write pointer. If not, we can only read up to the end of the buffer. 56256 */ 56257 if (readOffsetLoopFlag == writeOffsetLoopFlag) { 56258 bytesAvailable = writeOffsetInBytes - readOffsetInBytes; 56259 } else { 56260 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; 56261 } 56262 56263 bytesRequested = *pSizeInBytes; 56264 if (bytesRequested > bytesAvailable) { 56265 bytesRequested = bytesAvailable; 56266 } 56267 56268 *pSizeInBytes = bytesRequested; 56269 (*ppBufferOut) = ma_rb__get_read_ptr(pRB); 56270 56271 return MA_SUCCESS; 56272 } 56273 56274 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) 56275 { 56276 ma_uint32 readOffset; 56277 ma_uint32 readOffsetInBytes; 56278 ma_uint32 readOffsetLoopFlag; 56279 ma_uint32 newReadOffsetInBytes; 56280 ma_uint32 newReadOffsetLoopFlag; 56281 56282 if (pRB == NULL) { 56283 return MA_INVALID_ARGS; 56284 } 56285 56286 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56287 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56288 56289 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ 56290 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); 56291 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { 56292 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ 56293 } 56294 56295 /* Move the read pointer back to the start if necessary. */ 56296 newReadOffsetLoopFlag = readOffsetLoopFlag; 56297 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { 56298 newReadOffsetInBytes = 0; 56299 newReadOffsetLoopFlag ^= 0x80000000; 56300 } 56301 56302 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); 56303 56304 if (ma_rb_pointer_distance(pRB) == 0) { 56305 return MA_AT_END; 56306 } else { 56307 return MA_SUCCESS; 56308 } 56309 } 56310 56311 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) 56312 { 56313 ma_uint32 readOffset; 56314 ma_uint32 readOffsetInBytes; 56315 ma_uint32 readOffsetLoopFlag; 56316 ma_uint32 writeOffset; 56317 ma_uint32 writeOffsetInBytes; 56318 ma_uint32 writeOffsetLoopFlag; 56319 size_t bytesAvailable; 56320 size_t bytesRequested; 56321 56322 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { 56323 return MA_INVALID_ARGS; 56324 } 56325 56326 /* The returned buffer should never overtake the read buffer. */ 56327 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56328 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56329 56330 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56331 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56332 56333 /* 56334 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only 56335 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should 56336 never overtake the read pointer. 56337 */ 56338 if (writeOffsetLoopFlag == readOffsetLoopFlag) { 56339 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; 56340 } else { 56341 bytesAvailable = readOffsetInBytes - writeOffsetInBytes; 56342 } 56343 56344 bytesRequested = *pSizeInBytes; 56345 if (bytesRequested > bytesAvailable) { 56346 bytesRequested = bytesAvailable; 56347 } 56348 56349 *pSizeInBytes = bytesRequested; 56350 *ppBufferOut = ma_rb__get_write_ptr(pRB); 56351 56352 /* Clear the buffer if desired. */ 56353 if (pRB->clearOnWriteAcquire) { 56354 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); 56355 } 56356 56357 return MA_SUCCESS; 56358 } 56359 56360 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) 56361 { 56362 ma_uint32 writeOffset; 56363 ma_uint32 writeOffsetInBytes; 56364 ma_uint32 writeOffsetLoopFlag; 56365 ma_uint32 newWriteOffsetInBytes; 56366 ma_uint32 newWriteOffsetLoopFlag; 56367 56368 if (pRB == NULL) { 56369 return MA_INVALID_ARGS; 56370 } 56371 56372 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56373 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56374 56375 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ 56376 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); 56377 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { 56378 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ 56379 } 56380 56381 /* Move the read pointer back to the start if necessary. */ 56382 newWriteOffsetLoopFlag = writeOffsetLoopFlag; 56383 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { 56384 newWriteOffsetInBytes = 0; 56385 newWriteOffsetLoopFlag ^= 0x80000000; 56386 } 56387 56388 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); 56389 56390 if (ma_rb_pointer_distance(pRB) == 0) { 56391 return MA_AT_END; 56392 } else { 56393 return MA_SUCCESS; 56394 } 56395 } 56396 56397 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) 56398 { 56399 ma_uint32 readOffset; 56400 ma_uint32 readOffsetInBytes; 56401 ma_uint32 readOffsetLoopFlag; 56402 ma_uint32 writeOffset; 56403 ma_uint32 writeOffsetInBytes; 56404 ma_uint32 writeOffsetLoopFlag; 56405 ma_uint32 newReadOffsetInBytes; 56406 ma_uint32 newReadOffsetLoopFlag; 56407 56408 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { 56409 return MA_INVALID_ARGS; 56410 } 56411 56412 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56413 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56414 56415 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56416 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56417 56418 newReadOffsetLoopFlag = readOffsetLoopFlag; 56419 56420 /* We cannot go past the write buffer. */ 56421 if (readOffsetLoopFlag == writeOffsetLoopFlag) { 56422 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { 56423 newReadOffsetInBytes = writeOffsetInBytes; 56424 } else { 56425 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); 56426 } 56427 } else { 56428 /* May end up looping. */ 56429 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { 56430 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; 56431 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ 56432 } else { 56433 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); 56434 } 56435 } 56436 56437 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); 56438 return MA_SUCCESS; 56439 } 56440 56441 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) 56442 { 56443 ma_uint32 readOffset; 56444 ma_uint32 readOffsetInBytes; 56445 ma_uint32 readOffsetLoopFlag; 56446 ma_uint32 writeOffset; 56447 ma_uint32 writeOffsetInBytes; 56448 ma_uint32 writeOffsetLoopFlag; 56449 ma_uint32 newWriteOffsetInBytes; 56450 ma_uint32 newWriteOffsetLoopFlag; 56451 56452 if (pRB == NULL) { 56453 return MA_INVALID_ARGS; 56454 } 56455 56456 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56457 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56458 56459 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56460 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56461 56462 newWriteOffsetLoopFlag = writeOffsetLoopFlag; 56463 56464 /* We cannot go past the write buffer. */ 56465 if (readOffsetLoopFlag == writeOffsetLoopFlag) { 56466 /* May end up looping. */ 56467 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { 56468 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; 56469 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ 56470 } else { 56471 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); 56472 } 56473 } else { 56474 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { 56475 newWriteOffsetInBytes = readOffsetInBytes; 56476 } else { 56477 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); 56478 } 56479 } 56480 56481 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); 56482 return MA_SUCCESS; 56483 } 56484 56485 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) 56486 { 56487 ma_uint32 readOffset; 56488 ma_uint32 readOffsetInBytes; 56489 ma_uint32 readOffsetLoopFlag; 56490 ma_uint32 writeOffset; 56491 ma_uint32 writeOffsetInBytes; 56492 ma_uint32 writeOffsetLoopFlag; 56493 56494 if (pRB == NULL) { 56495 return 0; 56496 } 56497 56498 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); 56499 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); 56500 56501 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); 56502 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); 56503 56504 if (readOffsetLoopFlag == writeOffsetLoopFlag) { 56505 return writeOffsetInBytes - readOffsetInBytes; 56506 } else { 56507 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); 56508 } 56509 } 56510 56511 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) 56512 { 56513 ma_int32 dist; 56514 56515 if (pRB == NULL) { 56516 return 0; 56517 } 56518 56519 dist = ma_rb_pointer_distance(pRB); 56520 if (dist < 0) { 56521 return 0; 56522 } 56523 56524 return dist; 56525 } 56526 56527 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) 56528 { 56529 if (pRB == NULL) { 56530 return 0; 56531 } 56532 56533 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); 56534 } 56535 56536 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) 56537 { 56538 if (pRB == NULL) { 56539 return 0; 56540 } 56541 56542 return pRB->subbufferSizeInBytes; 56543 } 56544 56545 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) 56546 { 56547 if (pRB == NULL) { 56548 return 0; 56549 } 56550 56551 if (pRB->subbufferStrideInBytes == 0) { 56552 return (size_t)pRB->subbufferSizeInBytes; 56553 } 56554 56555 return (size_t)pRB->subbufferStrideInBytes; 56556 } 56557 56558 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) 56559 { 56560 if (pRB == NULL) { 56561 return 0; 56562 } 56563 56564 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); 56565 } 56566 56567 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) 56568 { 56569 if (pRB == NULL) { 56570 return NULL; 56571 } 56572 56573 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); 56574 } 56575 56576 56577 56578 static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 56579 { 56580 /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ 56581 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; 56582 ma_result result; 56583 ma_uint64 totalFramesRead; 56584 56585 MA_ASSERT(pRB != NULL); 56586 56587 /* We need to run this in a loop since the ring buffer itself may loop. */ 56588 totalFramesRead = 0; 56589 while (totalFramesRead < frameCount) { 56590 void* pMappedBuffer; 56591 ma_uint32 mappedFrameCount; 56592 ma_uint64 framesToRead = frameCount - totalFramesRead; 56593 if (framesToRead > 0xFFFFFFFF) { 56594 framesToRead = 0xFFFFFFFF; 56595 } 56596 56597 mappedFrameCount = (ma_uint32)framesToRead; 56598 result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); 56599 if (result != MA_SUCCESS) { 56600 break; 56601 } 56602 56603 if (mappedFrameCount == 0) { 56604 break; /* <-- End of ring buffer. */ 56605 } 56606 56607 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); 56608 56609 result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); 56610 if (result != MA_SUCCESS) { 56611 break; 56612 } 56613 56614 totalFramesRead += mappedFrameCount; 56615 } 56616 56617 *pFramesRead = totalFramesRead; 56618 return MA_SUCCESS; 56619 } 56620 56621 static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 56622 { 56623 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; 56624 MA_ASSERT(pRB != NULL); 56625 56626 if (pFormat != NULL) { 56627 *pFormat = pRB->format; 56628 } 56629 56630 if (pChannels != NULL) { 56631 *pChannels = pRB->channels; 56632 } 56633 56634 if (pSampleRate != NULL) { 56635 *pSampleRate = pRB->sampleRate; 56636 } 56637 56638 /* Just assume the default channel map. */ 56639 if (pChannelMap != NULL) { 56640 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); 56641 } 56642 56643 return MA_SUCCESS; 56644 } 56645 56646 static ma_data_source_vtable ma_gRBDataSourceVTable = 56647 { 56648 ma_pcm_rb_data_source__on_read, 56649 NULL, /* onSeek */ 56650 ma_pcm_rb_data_source__on_get_data_format, 56651 NULL, /* onGetCursor */ 56652 NULL, /* onGetLength */ 56653 NULL, /* onSetLooping */ 56654 0 56655 }; 56656 56657 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) 56658 { 56659 MA_ASSERT(pRB != NULL); 56660 56661 return ma_get_bytes_per_frame(pRB->format, pRB->channels); 56662 } 56663 56664 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) 56665 { 56666 ma_uint32 bpf; 56667 ma_result result; 56668 56669 if (pRB == NULL) { 56670 return MA_INVALID_ARGS; 56671 } 56672 56673 MA_ZERO_OBJECT(pRB); 56674 56675 bpf = ma_get_bytes_per_frame(format, channels); 56676 if (bpf == 0) { 56677 return MA_INVALID_ARGS; 56678 } 56679 56680 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); 56681 if (result != MA_SUCCESS) { 56682 return result; 56683 } 56684 56685 pRB->format = format; 56686 pRB->channels = channels; 56687 pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ 56688 56689 /* The PCM ring buffer is a data source. We need to get that set up as well. */ 56690 { 56691 ma_data_source_config dataSourceConfig = ma_data_source_config_init(); 56692 dataSourceConfig.vtable = &ma_gRBDataSourceVTable; 56693 56694 result = ma_data_source_init(&dataSourceConfig, &pRB->ds); 56695 if (result != MA_SUCCESS) { 56696 ma_rb_uninit(&pRB->rb); 56697 return result; 56698 } 56699 } 56700 56701 return MA_SUCCESS; 56702 } 56703 56704 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) 56705 { 56706 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); 56707 } 56708 56709 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) 56710 { 56711 if (pRB == NULL) { 56712 return; 56713 } 56714 56715 ma_data_source_uninit(&pRB->ds); 56716 ma_rb_uninit(&pRB->rb); 56717 } 56718 56719 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) 56720 { 56721 if (pRB == NULL) { 56722 return; 56723 } 56724 56725 ma_rb_reset(&pRB->rb); 56726 } 56727 56728 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) 56729 { 56730 size_t sizeInBytes; 56731 ma_result result; 56732 56733 if (pRB == NULL || pSizeInFrames == NULL) { 56734 return MA_INVALID_ARGS; 56735 } 56736 56737 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); 56738 56739 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); 56740 if (result != MA_SUCCESS) { 56741 return result; 56742 } 56743 56744 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); 56745 return MA_SUCCESS; 56746 } 56747 56748 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) 56749 { 56750 if (pRB == NULL) { 56751 return MA_INVALID_ARGS; 56752 } 56753 56754 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); 56755 } 56756 56757 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) 56758 { 56759 size_t sizeInBytes; 56760 ma_result result; 56761 56762 if (pRB == NULL) { 56763 return MA_INVALID_ARGS; 56764 } 56765 56766 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); 56767 56768 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); 56769 if (result != MA_SUCCESS) { 56770 return result; 56771 } 56772 56773 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); 56774 return MA_SUCCESS; 56775 } 56776 56777 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) 56778 { 56779 if (pRB == NULL) { 56780 return MA_INVALID_ARGS; 56781 } 56782 56783 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); 56784 } 56785 56786 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) 56787 { 56788 if (pRB == NULL) { 56789 return MA_INVALID_ARGS; 56790 } 56791 56792 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); 56793 } 56794 56795 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) 56796 { 56797 if (pRB == NULL) { 56798 return MA_INVALID_ARGS; 56799 } 56800 56801 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); 56802 } 56803 56804 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) 56805 { 56806 if (pRB == NULL) { 56807 return 0; 56808 } 56809 56810 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); 56811 } 56812 56813 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) 56814 { 56815 if (pRB == NULL) { 56816 return 0; 56817 } 56818 56819 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); 56820 } 56821 56822 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) 56823 { 56824 if (pRB == NULL) { 56825 return 0; 56826 } 56827 56828 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); 56829 } 56830 56831 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) 56832 { 56833 if (pRB == NULL) { 56834 return 0; 56835 } 56836 56837 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); 56838 } 56839 56840 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) 56841 { 56842 if (pRB == NULL) { 56843 return 0; 56844 } 56845 56846 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); 56847 } 56848 56849 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) 56850 { 56851 if (pRB == NULL) { 56852 return 0; 56853 } 56854 56855 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); 56856 } 56857 56858 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) 56859 { 56860 if (pRB == NULL) { 56861 return NULL; 56862 } 56863 56864 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); 56865 } 56866 56867 MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) 56868 { 56869 if (pRB == NULL) { 56870 return ma_format_unknown; 56871 } 56872 56873 return pRB->format; 56874 } 56875 56876 MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) 56877 { 56878 if (pRB == NULL) { 56879 return 0; 56880 } 56881 56882 return pRB->channels; 56883 } 56884 56885 MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) 56886 { 56887 if (pRB == NULL) { 56888 return 0; 56889 } 56890 56891 return pRB->sampleRate; 56892 } 56893 56894 MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) 56895 { 56896 if (pRB == NULL) { 56897 return; 56898 } 56899 56900 pRB->sampleRate = sampleRate; 56901 } 56902 56903 56904 56905 MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) 56906 { 56907 ma_result result; 56908 ma_uint32 sizeInFrames; 56909 56910 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); 56911 if (sizeInFrames == 0) { 56912 return MA_INVALID_ARGS; 56913 } 56914 56915 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); 56916 if (result != MA_SUCCESS) { 56917 return result; 56918 } 56919 56920 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ 56921 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); 56922 56923 return MA_SUCCESS; 56924 } 56925 56926 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) 56927 { 56928 ma_pcm_rb_uninit((ma_pcm_rb*)pRB); 56929 return MA_SUCCESS; 56930 } 56931 56932 56933 56934 /************************************************************************************************************************************************************** 56935 56936 Miscellaneous Helpers 56937 56938 **************************************************************************************************************************************************************/ 56939 MA_API const char* ma_result_description(ma_result result) 56940 { 56941 switch (result) 56942 { 56943 case MA_SUCCESS: return "No error"; 56944 case MA_ERROR: return "Unknown error"; 56945 case MA_INVALID_ARGS: return "Invalid argument"; 56946 case MA_INVALID_OPERATION: return "Invalid operation"; 56947 case MA_OUT_OF_MEMORY: return "Out of memory"; 56948 case MA_OUT_OF_RANGE: return "Out of range"; 56949 case MA_ACCESS_DENIED: return "Permission denied"; 56950 case MA_DOES_NOT_EXIST: return "Resource does not exist"; 56951 case MA_ALREADY_EXISTS: return "Resource already exists"; 56952 case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; 56953 case MA_INVALID_FILE: return "Invalid file"; 56954 case MA_TOO_BIG: return "Too large"; 56955 case MA_PATH_TOO_LONG: return "Path too long"; 56956 case MA_NAME_TOO_LONG: return "Name too long"; 56957 case MA_NOT_DIRECTORY: return "Not a directory"; 56958 case MA_IS_DIRECTORY: return "Is a directory"; 56959 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; 56960 case MA_AT_END: return "At end"; 56961 case MA_NO_SPACE: return "No space available"; 56962 case MA_BUSY: return "Device or resource busy"; 56963 case MA_IO_ERROR: return "Input/output error"; 56964 case MA_INTERRUPT: return "Interrupted"; 56965 case MA_UNAVAILABLE: return "Resource unavailable"; 56966 case MA_ALREADY_IN_USE: return "Resource already in use"; 56967 case MA_BAD_ADDRESS: return "Bad address"; 56968 case MA_BAD_SEEK: return "Illegal seek"; 56969 case MA_BAD_PIPE: return "Broken pipe"; 56970 case MA_DEADLOCK: return "Deadlock"; 56971 case MA_TOO_MANY_LINKS: return "Too many links"; 56972 case MA_NOT_IMPLEMENTED: return "Not implemented"; 56973 case MA_NO_MESSAGE: return "No message of desired type"; 56974 case MA_BAD_MESSAGE: return "Invalid message"; 56975 case MA_NO_DATA_AVAILABLE: return "No data available"; 56976 case MA_INVALID_DATA: return "Invalid data"; 56977 case MA_TIMEOUT: return "Timeout"; 56978 case MA_NO_NETWORK: return "Network unavailable"; 56979 case MA_NOT_UNIQUE: return "Not unique"; 56980 case MA_NOT_SOCKET: return "Socket operation on non-socket"; 56981 case MA_NO_ADDRESS: return "Destination address required"; 56982 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; 56983 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; 56984 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; 56985 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; 56986 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; 56987 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; 56988 case MA_CONNECTION_RESET: return "Connection reset"; 56989 case MA_ALREADY_CONNECTED: return "Already connected"; 56990 case MA_NOT_CONNECTED: return "Not connected"; 56991 case MA_CONNECTION_REFUSED: return "Connection refused"; 56992 case MA_NO_HOST: return "No host"; 56993 case MA_IN_PROGRESS: return "Operation in progress"; 56994 case MA_CANCELLED: return "Operation cancelled"; 56995 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; 56996 56997 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; 56998 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; 56999 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; 57000 case MA_NO_BACKEND: return "No backend"; 57001 case MA_NO_DEVICE: return "No device"; 57002 case MA_API_NOT_FOUND: return "API not found"; 57003 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; 57004 57005 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; 57006 case MA_DEVICE_NOT_STARTED: return "Device not started"; 57007 57008 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; 57009 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; 57010 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; 57011 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; 57012 57013 default: return "Unknown error"; 57014 } 57015 } 57016 57017 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 57018 { 57019 if (pAllocationCallbacks != NULL) { 57020 if (pAllocationCallbacks->onMalloc != NULL) { 57021 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); 57022 } else { 57023 return NULL; /* Do not fall back to the default implementation. */ 57024 } 57025 } else { 57026 return ma__malloc_default(sz, NULL); 57027 } 57028 } 57029 57030 MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 57031 { 57032 void* p = ma_malloc(sz, pAllocationCallbacks); 57033 if (p != NULL) { 57034 MA_ZERO_MEMORY(p, sz); 57035 } 57036 57037 return p; 57038 } 57039 57040 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 57041 { 57042 if (pAllocationCallbacks != NULL) { 57043 if (pAllocationCallbacks->onRealloc != NULL) { 57044 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); 57045 } else { 57046 return NULL; /* Do not fall back to the default implementation. */ 57047 } 57048 } else { 57049 return ma__realloc_default(p, sz, NULL); 57050 } 57051 } 57052 57053 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 57054 { 57055 if (p == NULL) { 57056 return; 57057 } 57058 57059 if (pAllocationCallbacks != NULL) { 57060 if (pAllocationCallbacks->onFree != NULL) { 57061 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 57062 } else { 57063 return; /* Do no fall back to the default implementation. */ 57064 } 57065 } else { 57066 ma__free_default(p, NULL); 57067 } 57068 } 57069 57070 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) 57071 { 57072 size_t extraBytes; 57073 void* pUnaligned; 57074 void* pAligned; 57075 57076 if (alignment == 0) { 57077 return 0; 57078 } 57079 57080 extraBytes = alignment-1 + sizeof(void*); 57081 57082 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); 57083 if (pUnaligned == NULL) { 57084 return NULL; 57085 } 57086 57087 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); 57088 ((void**)pAligned)[-1] = pUnaligned; 57089 57090 return pAligned; 57091 } 57092 57093 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 57094 { 57095 ma_free(((void**)p)[-1], pAllocationCallbacks); 57096 } 57097 57098 MA_API const char* ma_get_format_name(ma_format format) 57099 { 57100 switch (format) 57101 { 57102 case ma_format_unknown: return "Unknown"; 57103 case ma_format_u8: return "8-bit Unsigned Integer"; 57104 case ma_format_s16: return "16-bit Signed Integer"; 57105 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; 57106 case ma_format_s32: return "32-bit Signed Integer"; 57107 case ma_format_f32: return "32-bit IEEE Floating Point"; 57108 default: return "Invalid"; 57109 } 57110 } 57111 57112 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) 57113 { 57114 ma_uint32 i; 57115 for (i = 0; i < channels; ++i) { 57116 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); 57117 } 57118 } 57119 57120 57121 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) 57122 { 57123 ma_uint32 sizes[] = { 57124 0, /* unknown */ 57125 1, /* u8 */ 57126 2, /* s16 */ 57127 3, /* s24 */ 57128 4, /* s32 */ 57129 4, /* f32 */ 57130 }; 57131 return sizes[format]; 57132 } 57133 57134 57135 57136 #define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 57137 #define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) 57138 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 57139 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) 57140 57141 MA_API ma_data_source_config ma_data_source_config_init(void) 57142 { 57143 ma_data_source_config config; 57144 57145 MA_ZERO_OBJECT(&config); 57146 57147 return config; 57148 } 57149 57150 57151 MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) 57152 { 57153 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57154 57155 if (pDataSource == NULL) { 57156 return MA_INVALID_ARGS; 57157 } 57158 57159 MA_ZERO_OBJECT(pDataSourceBase); 57160 57161 if (pConfig == NULL) { 57162 return MA_INVALID_ARGS; 57163 } 57164 57165 pDataSourceBase->vtable = pConfig->vtable; 57166 pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; 57167 pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; 57168 pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; 57169 pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; 57170 pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ 57171 pDataSourceBase->pNext = NULL; 57172 pDataSourceBase->onGetNext = NULL; 57173 57174 return MA_SUCCESS; 57175 } 57176 57177 MA_API void ma_data_source_uninit(ma_data_source* pDataSource) 57178 { 57179 if (pDataSource == NULL) { 57180 return; 57181 } 57182 57183 /* 57184 This is placeholder in case we need this later. Data sources need to call this in their 57185 uninitialization routine to ensure things work later on if something is added here. 57186 */ 57187 } 57188 57189 static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) 57190 { 57191 ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; 57192 57193 MA_ASSERT(pDataSource != NULL); 57194 MA_ASSERT(ppCurrentDataSource != NULL); 57195 57196 if (pCurrentDataSource->pCurrent == NULL) { 57197 /* 57198 The current data source is NULL. If we're using this in the context of a chain we need to return NULL 57199 here so that we don't end up looping. Otherwise we just return the data source itself. 57200 */ 57201 if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { 57202 pCurrentDataSource = NULL; 57203 } else { 57204 pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ 57205 } 57206 } else { 57207 pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; 57208 } 57209 57210 *ppCurrentDataSource = pCurrentDataSource; 57211 57212 return MA_SUCCESS; 57213 } 57214 57215 static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 57216 { 57217 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57218 ma_result result; 57219 ma_uint64 framesRead = 0; 57220 ma_bool32 loop = ma_data_source_is_looping(pDataSource); 57221 57222 if (pDataSourceBase == NULL) { 57223 return MA_AT_END; 57224 } 57225 57226 if (frameCount == 0) { 57227 return MA_INVALID_ARGS; 57228 } 57229 57230 if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { 57231 /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ 57232 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); 57233 } else { 57234 /* Need to clamp to within the range. */ 57235 ma_uint64 relativeCursor; 57236 ma_uint64 absoluteCursor; 57237 57238 result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); 57239 if (result != MA_SUCCESS) { 57240 /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ 57241 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); 57242 } else { 57243 ma_uint64 rangeBeg; 57244 ma_uint64 rangeEnd; 57245 57246 /* We have the cursor. We need to make sure we don't read beyond our range. */ 57247 rangeBeg = pDataSourceBase->rangeBegInFrames; 57248 rangeEnd = pDataSourceBase->rangeEndInFrames; 57249 57250 absoluteCursor = rangeBeg + relativeCursor; 57251 57252 /* If looping, make sure we're within range. */ 57253 if (loop) { 57254 if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { 57255 rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); 57256 } 57257 } 57258 57259 if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { 57260 frameCount = (rangeEnd - absoluteCursor); 57261 } 57262 57263 /* 57264 If the cursor is sitting on the end of the range the frame count will be set to 0 which can 57265 result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return 57266 MA_AT_END so the higher level function can know about it. 57267 */ 57268 if (frameCount > 0) { 57269 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); 57270 } else { 57271 result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ 57272 } 57273 } 57274 } 57275 57276 if (pFramesRead != NULL) { 57277 *pFramesRead = framesRead; 57278 } 57279 57280 /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ 57281 if (result == MA_SUCCESS && framesRead == 0) { 57282 result = MA_AT_END; 57283 } 57284 57285 return result; 57286 } 57287 57288 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 57289 { 57290 ma_result result = MA_SUCCESS; 57291 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57292 ma_data_source_base* pCurrentDataSource; 57293 void* pRunningFramesOut = pFramesOut; 57294 ma_uint64 totalFramesProcessed = 0; 57295 ma_format format; 57296 ma_uint32 channels; 57297 ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ 57298 ma_bool32 loop; 57299 57300 if (pFramesRead != NULL) { 57301 *pFramesRead = 0; 57302 } 57303 57304 if (frameCount == 0) { 57305 return MA_INVALID_ARGS; 57306 } 57307 57308 if (pDataSourceBase == NULL) { 57309 return MA_INVALID_ARGS; 57310 } 57311 57312 loop = ma_data_source_is_looping(pDataSource); 57313 57314 /* 57315 We need to know the data format so we can advance the output buffer as we read frames. If this 57316 fails, chaining will not work and we'll just read as much as we can from the current source. 57317 */ 57318 if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { 57319 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); 57320 if (result != MA_SUCCESS) { 57321 return result; 57322 } 57323 57324 return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); 57325 } 57326 57327 /* 57328 Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and 57329 only the current data source will be read from. 57330 */ 57331 57332 /* Keep reading until we've read as many frames as possible. */ 57333 while (totalFramesProcessed < frameCount) { 57334 ma_uint64 framesProcessed; 57335 ma_uint64 framesRemaining = frameCount - totalFramesProcessed; 57336 57337 /* We need to resolve the data source that we'll actually be reading from. */ 57338 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); 57339 if (result != MA_SUCCESS) { 57340 break; 57341 } 57342 57343 if (pCurrentDataSource == NULL) { 57344 break; 57345 } 57346 57347 result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); 57348 totalFramesProcessed += framesProcessed; 57349 57350 /* 57351 If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is 57352 not necessarily considered an error. 57353 */ 57354 if (result != MA_SUCCESS && result != MA_AT_END) { 57355 break; 57356 } 57357 57358 /* 57359 We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned 57360 MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. 57361 */ 57362 if (result == MA_AT_END) { 57363 /* 57364 The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't 57365 accidentally return MA_AT_END when data has been read in prior loop iterations. at the 57366 end of this function, the result will be checked for MA_SUCCESS, and if the total 57367 number of frames processed is 0, will be explicitly set to MA_AT_END. 57368 */ 57369 result = MA_SUCCESS; 57370 57371 /* 57372 We reached the end. If we're looping, we just loop back to the start of the current 57373 data source. If we're not looping we need to check if we have another in the chain, and 57374 if so, switch to it. 57375 */ 57376 if (loop) { 57377 if (framesProcessed == 0) { 57378 emptyLoopCounter += 1; 57379 if (emptyLoopCounter > 1) { 57380 break; /* Infinite loop detected. Get out. */ 57381 } 57382 } else { 57383 emptyLoopCounter = 0; 57384 } 57385 57386 result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); 57387 if (result != MA_SUCCESS) { 57388 break; /* Failed to loop. Abort. */ 57389 } 57390 57391 /* Don't return MA_AT_END for looping sounds. */ 57392 result = MA_SUCCESS; 57393 } else { 57394 if (pCurrentDataSource->pNext != NULL) { 57395 pDataSourceBase->pCurrent = pCurrentDataSource->pNext; 57396 } else if (pCurrentDataSource->onGetNext != NULL) { 57397 pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); 57398 if (pDataSourceBase->pCurrent == NULL) { 57399 break; /* Our callback did not return a next data source. We're done. */ 57400 } 57401 } else { 57402 /* Reached the end of the chain. We're done. */ 57403 break; 57404 } 57405 57406 /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ 57407 result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); 57408 if (result != MA_SUCCESS) { 57409 break; 57410 } 57411 } 57412 } 57413 57414 if (pRunningFramesOut != NULL) { 57415 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); 57416 } 57417 } 57418 57419 if (pFramesRead != NULL) { 57420 *pFramesRead = totalFramesProcessed; 57421 } 57422 57423 MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ 57424 57425 if (result == MA_SUCCESS && totalFramesProcessed == 0) { 57426 result = MA_AT_END; 57427 } 57428 57429 return result; 57430 } 57431 57432 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) 57433 { 57434 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); 57435 } 57436 57437 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) 57438 { 57439 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57440 57441 if (pDataSourceBase == NULL) { 57442 return MA_SUCCESS; 57443 } 57444 57445 if (pDataSourceBase->vtable->onSeek == NULL) { 57446 return MA_NOT_IMPLEMENTED; 57447 } 57448 57449 if (frameIndex > pDataSourceBase->rangeEndInFrames) { 57450 return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ 57451 } 57452 57453 return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); 57454 } 57455 57456 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 57457 { 57458 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57459 ma_result result; 57460 ma_format format; 57461 ma_uint32 channels; 57462 ma_uint32 sampleRate; 57463 57464 /* Initialize to defaults for safety just in case the data source does not implement this callback. */ 57465 if (pFormat != NULL) { 57466 *pFormat = ma_format_unknown; 57467 } 57468 if (pChannels != NULL) { 57469 *pChannels = 0; 57470 } 57471 if (pSampleRate != NULL) { 57472 *pSampleRate = 0; 57473 } 57474 if (pChannelMap != NULL) { 57475 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 57476 } 57477 57478 if (pDataSourceBase == NULL) { 57479 return MA_INVALID_ARGS; 57480 } 57481 57482 if (pDataSourceBase->vtable->onGetDataFormat == NULL) { 57483 return MA_NOT_IMPLEMENTED; 57484 } 57485 57486 result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); 57487 if (result != MA_SUCCESS) { 57488 return result; 57489 } 57490 57491 if (pFormat != NULL) { 57492 *pFormat = format; 57493 } 57494 if (pChannels != NULL) { 57495 *pChannels = channels; 57496 } 57497 if (pSampleRate != NULL) { 57498 *pSampleRate = sampleRate; 57499 } 57500 57501 /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ 57502 57503 return MA_SUCCESS; 57504 } 57505 57506 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) 57507 { 57508 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57509 ma_result result; 57510 ma_uint64 cursor; 57511 57512 if (pCursor == NULL) { 57513 return MA_INVALID_ARGS; 57514 } 57515 57516 *pCursor = 0; 57517 57518 if (pDataSourceBase == NULL) { 57519 return MA_SUCCESS; 57520 } 57521 57522 if (pDataSourceBase->vtable->onGetCursor == NULL) { 57523 return MA_NOT_IMPLEMENTED; 57524 } 57525 57526 result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); 57527 if (result != MA_SUCCESS) { 57528 return result; 57529 } 57530 57531 /* The cursor needs to be made relative to the start of the range. */ 57532 if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ 57533 *pCursor = 0; 57534 } else { 57535 *pCursor = cursor - pDataSourceBase->rangeBegInFrames; 57536 } 57537 57538 return MA_SUCCESS; 57539 } 57540 57541 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) 57542 { 57543 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57544 57545 if (pLength == NULL) { 57546 return MA_INVALID_ARGS; 57547 } 57548 57549 *pLength = 0; 57550 57551 if (pDataSourceBase == NULL) { 57552 return MA_INVALID_ARGS; 57553 } 57554 57555 /* 57556 If we have a range defined we'll use that to determine the length. This is one of rare times 57557 where we'll actually trust the caller. If they've set the range, I think it's mostly safe to 57558 assume they've set it based on some higher level knowledge of the structure of the sound bank. 57559 */ 57560 if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { 57561 *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; 57562 return MA_SUCCESS; 57563 } 57564 57565 /* 57566 Getting here means a range is not defined so we'll need to get the data source itself to tell 57567 us the length. 57568 */ 57569 if (pDataSourceBase->vtable->onGetLength == NULL) { 57570 return MA_NOT_IMPLEMENTED; 57571 } 57572 57573 return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); 57574 } 57575 57576 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) 57577 { 57578 ma_result result; 57579 ma_uint64 cursorInPCMFrames; 57580 ma_uint32 sampleRate; 57581 57582 if (pCursor == NULL) { 57583 return MA_INVALID_ARGS; 57584 } 57585 57586 *pCursor = 0; 57587 57588 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); 57589 if (result != MA_SUCCESS) { 57590 return result; 57591 } 57592 57593 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); 57594 if (result != MA_SUCCESS) { 57595 return result; 57596 } 57597 57598 /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ 57599 *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; 57600 57601 return MA_SUCCESS; 57602 } 57603 57604 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) 57605 { 57606 ma_result result; 57607 ma_uint64 lengthInPCMFrames; 57608 ma_uint32 sampleRate; 57609 57610 if (pLength == NULL) { 57611 return MA_INVALID_ARGS; 57612 } 57613 57614 *pLength = 0; 57615 57616 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); 57617 if (result != MA_SUCCESS) { 57618 return result; 57619 } 57620 57621 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); 57622 if (result != MA_SUCCESS) { 57623 return result; 57624 } 57625 57626 /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ 57627 *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; 57628 57629 return MA_SUCCESS; 57630 } 57631 57632 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) 57633 { 57634 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57635 57636 if (pDataSource == NULL) { 57637 return MA_INVALID_ARGS; 57638 } 57639 57640 ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); 57641 57642 /* If there's no callback for this just treat it as a successful no-op. */ 57643 if (pDataSourceBase->vtable->onSetLooping == NULL) { 57644 return MA_SUCCESS; 57645 } 57646 57647 return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); 57648 } 57649 57650 MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) 57651 { 57652 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57653 57654 if (pDataSource == NULL) { 57655 return MA_FALSE; 57656 } 57657 57658 return ma_atomic_load_32(&pDataSourceBase->isLooping); 57659 } 57660 57661 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) 57662 { 57663 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57664 ma_result result; 57665 ma_uint64 relativeCursor; 57666 ma_uint64 absoluteCursor; 57667 ma_bool32 doSeekAdjustment = MA_FALSE; 57668 57669 if (pDataSource == NULL) { 57670 return MA_INVALID_ARGS; 57671 } 57672 57673 if (rangeEndInFrames < rangeBegInFrames) { 57674 return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ 57675 } 57676 57677 /* 57678 We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now 57679 so we can calculate it's absolute position before we change the range. 57680 */ 57681 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); 57682 if (result == MA_SUCCESS) { 57683 doSeekAdjustment = MA_TRUE; 57684 absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; 57685 } else { 57686 /* 57687 We couldn't get the position of the cursor. It probably means the data source has no notion 57688 of a cursor. We'll just leave it at position 0. Don't treat this as an error. 57689 */ 57690 doSeekAdjustment = MA_FALSE; 57691 relativeCursor = 0; 57692 absoluteCursor = 0; 57693 } 57694 57695 pDataSourceBase->rangeBegInFrames = rangeBegInFrames; 57696 pDataSourceBase->rangeEndInFrames = rangeEndInFrames; 57697 57698 /* 57699 The commented out logic below was intended to maintain loop points in response to a change in the 57700 range. However, this is not useful because it results in the sound breaking when you move the range 57701 outside of the old loop points. I'm simplifying this by simply resetting the loop points. The 57702 caller is expected to update their loop points if they change the range. 57703 57704 In practice this should be mostly a non-issue because the majority of the time the range will be 57705 set once right after initialization. 57706 */ 57707 pDataSourceBase->loopBegInFrames = 0; 57708 pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); 57709 57710 57711 /* 57712 Seek to within range. Note that our seek positions here are relative to the new range. We don't want 57713 do do this if we failed to retrieve the cursor earlier on because it probably means the data source 57714 has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but 57715 I'm just not even going to attempt it. 57716 */ 57717 if (doSeekAdjustment) { 57718 if (absoluteCursor < rangeBegInFrames) { 57719 ma_data_source_seek_to_pcm_frame(pDataSource, 0); 57720 } else if (absoluteCursor > rangeEndInFrames) { 57721 ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); 57722 } 57723 } 57724 57725 return MA_SUCCESS; 57726 } 57727 57728 MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) 57729 { 57730 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57731 57732 if (pDataSource == NULL) { 57733 return; 57734 } 57735 57736 if (pRangeBegInFrames != NULL) { 57737 *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; 57738 } 57739 57740 if (pRangeEndInFrames != NULL) { 57741 *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; 57742 } 57743 } 57744 57745 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) 57746 { 57747 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57748 57749 if (pDataSource == NULL) { 57750 return MA_INVALID_ARGS; 57751 } 57752 57753 if (loopEndInFrames < loopBegInFrames) { 57754 return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ 57755 } 57756 57757 if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { 57758 return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ 57759 } 57760 57761 pDataSourceBase->loopBegInFrames = loopBegInFrames; 57762 pDataSourceBase->loopEndInFrames = loopEndInFrames; 57763 57764 /* The end cannot exceed the range. */ 57765 if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { 57766 pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); 57767 } 57768 57769 return MA_SUCCESS; 57770 } 57771 57772 MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) 57773 { 57774 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57775 57776 if (pDataSource == NULL) { 57777 return; 57778 } 57779 57780 if (pLoopBegInFrames != NULL) { 57781 *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; 57782 } 57783 57784 if (pLoopEndInFrames != NULL) { 57785 *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; 57786 } 57787 } 57788 57789 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) 57790 { 57791 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57792 57793 if (pDataSource == NULL) { 57794 return MA_INVALID_ARGS; 57795 } 57796 57797 pDataSourceBase->pCurrent = pCurrentDataSource; 57798 57799 return MA_SUCCESS; 57800 } 57801 57802 MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) 57803 { 57804 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57805 57806 if (pDataSource == NULL) { 57807 return NULL; 57808 } 57809 57810 return pDataSourceBase->pCurrent; 57811 } 57812 57813 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) 57814 { 57815 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57816 57817 if (pDataSource == NULL) { 57818 return MA_INVALID_ARGS; 57819 } 57820 57821 pDataSourceBase->pNext = pNextDataSource; 57822 57823 return MA_SUCCESS; 57824 } 57825 57826 MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) 57827 { 57828 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57829 57830 if (pDataSource == NULL) { 57831 return NULL; 57832 } 57833 57834 return pDataSourceBase->pNext; 57835 } 57836 57837 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) 57838 { 57839 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; 57840 57841 if (pDataSource == NULL) { 57842 return MA_INVALID_ARGS; 57843 } 57844 57845 pDataSourceBase->onGetNext = onGetNext; 57846 57847 return MA_SUCCESS; 57848 } 57849 57850 MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) 57851 { 57852 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; 57853 57854 if (pDataSource == NULL) { 57855 return NULL; 57856 } 57857 57858 return pDataSourceBase->onGetNext; 57859 } 57860 57861 57862 static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 57863 { 57864 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; 57865 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); 57866 57867 if (pFramesRead != NULL) { 57868 *pFramesRead = framesRead; 57869 } 57870 57871 if (framesRead < frameCount || framesRead == 0) { 57872 return MA_AT_END; 57873 } 57874 57875 return MA_SUCCESS; 57876 } 57877 57878 static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 57879 { 57880 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); 57881 } 57882 57883 static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 57884 { 57885 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; 57886 57887 *pFormat = pAudioBufferRef->format; 57888 *pChannels = pAudioBufferRef->channels; 57889 *pSampleRate = pAudioBufferRef->sampleRate; 57890 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); 57891 57892 return MA_SUCCESS; 57893 } 57894 57895 static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 57896 { 57897 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; 57898 57899 *pCursor = pAudioBufferRef->cursor; 57900 57901 return MA_SUCCESS; 57902 } 57903 57904 static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 57905 { 57906 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; 57907 57908 *pLength = pAudioBufferRef->sizeInFrames; 57909 57910 return MA_SUCCESS; 57911 } 57912 57913 static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = 57914 { 57915 ma_audio_buffer_ref__data_source_on_read, 57916 ma_audio_buffer_ref__data_source_on_seek, 57917 ma_audio_buffer_ref__data_source_on_get_data_format, 57918 ma_audio_buffer_ref__data_source_on_get_cursor, 57919 ma_audio_buffer_ref__data_source_on_get_length, 57920 NULL, /* onSetLooping */ 57921 0 57922 }; 57923 57924 MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) 57925 { 57926 ma_result result; 57927 ma_data_source_config dataSourceConfig; 57928 57929 if (pAudioBufferRef == NULL) { 57930 return MA_INVALID_ARGS; 57931 } 57932 57933 MA_ZERO_OBJECT(pAudioBufferRef); 57934 57935 dataSourceConfig = ma_data_source_config_init(); 57936 dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; 57937 57938 result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); 57939 if (result != MA_SUCCESS) { 57940 return result; 57941 } 57942 57943 pAudioBufferRef->format = format; 57944 pAudioBufferRef->channels = channels; 57945 pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ 57946 pAudioBufferRef->cursor = 0; 57947 pAudioBufferRef->sizeInFrames = sizeInFrames; 57948 pAudioBufferRef->pData = pData; 57949 57950 return MA_SUCCESS; 57951 } 57952 57953 MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) 57954 { 57955 if (pAudioBufferRef == NULL) { 57956 return; 57957 } 57958 57959 ma_data_source_uninit(&pAudioBufferRef->ds); 57960 } 57961 57962 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) 57963 { 57964 if (pAudioBufferRef == NULL) { 57965 return MA_INVALID_ARGS; 57966 } 57967 57968 pAudioBufferRef->cursor = 0; 57969 pAudioBufferRef->sizeInFrames = sizeInFrames; 57970 pAudioBufferRef->pData = pData; 57971 57972 return MA_SUCCESS; 57973 } 57974 57975 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) 57976 { 57977 ma_uint64 totalFramesRead = 0; 57978 57979 if (pAudioBufferRef == NULL) { 57980 return 0; 57981 } 57982 57983 if (frameCount == 0) { 57984 return 0; 57985 } 57986 57987 while (totalFramesRead < frameCount) { 57988 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; 57989 ma_uint64 framesRemaining = frameCount - totalFramesRead; 57990 ma_uint64 framesToRead; 57991 57992 framesToRead = framesRemaining; 57993 if (framesToRead > framesAvailable) { 57994 framesToRead = framesAvailable; 57995 } 57996 57997 if (pFramesOut != NULL) { 57998 ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); 57999 } 58000 58001 totalFramesRead += framesToRead; 58002 58003 pAudioBufferRef->cursor += framesToRead; 58004 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { 58005 if (loop) { 58006 pAudioBufferRef->cursor = 0; 58007 } else { 58008 break; /* We've reached the end and we're not looping. Done. */ 58009 } 58010 } 58011 58012 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); 58013 } 58014 58015 return totalFramesRead; 58016 } 58017 58018 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) 58019 { 58020 if (pAudioBufferRef == NULL) { 58021 return MA_INVALID_ARGS; 58022 } 58023 58024 if (frameIndex > pAudioBufferRef->sizeInFrames) { 58025 return MA_INVALID_ARGS; 58026 } 58027 58028 pAudioBufferRef->cursor = (size_t)frameIndex; 58029 58030 return MA_SUCCESS; 58031 } 58032 58033 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) 58034 { 58035 ma_uint64 framesAvailable; 58036 ma_uint64 frameCount = 0; 58037 58038 if (ppFramesOut != NULL) { 58039 *ppFramesOut = NULL; /* Safety. */ 58040 } 58041 58042 if (pFrameCount != NULL) { 58043 frameCount = *pFrameCount; 58044 *pFrameCount = 0; /* Safety. */ 58045 } 58046 58047 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { 58048 return MA_INVALID_ARGS; 58049 } 58050 58051 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; 58052 if (frameCount > framesAvailable) { 58053 frameCount = framesAvailable; 58054 } 58055 58056 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); 58057 *pFrameCount = frameCount; 58058 58059 return MA_SUCCESS; 58060 } 58061 58062 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) 58063 { 58064 ma_uint64 framesAvailable; 58065 58066 if (pAudioBufferRef == NULL) { 58067 return MA_INVALID_ARGS; 58068 } 58069 58070 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; 58071 if (frameCount > framesAvailable) { 58072 return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ 58073 } 58074 58075 pAudioBufferRef->cursor += frameCount; 58076 58077 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { 58078 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ 58079 } else { 58080 return MA_SUCCESS; 58081 } 58082 } 58083 58084 MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) 58085 { 58086 if (pAudioBufferRef == NULL) { 58087 return MA_FALSE; 58088 } 58089 58090 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; 58091 } 58092 58093 MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) 58094 { 58095 if (pCursor == NULL) { 58096 return MA_INVALID_ARGS; 58097 } 58098 58099 *pCursor = 0; 58100 58101 if (pAudioBufferRef == NULL) { 58102 return MA_INVALID_ARGS; 58103 } 58104 58105 *pCursor = pAudioBufferRef->cursor; 58106 58107 return MA_SUCCESS; 58108 } 58109 58110 MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) 58111 { 58112 if (pLength == NULL) { 58113 return MA_INVALID_ARGS; 58114 } 58115 58116 *pLength = 0; 58117 58118 if (pAudioBufferRef == NULL) { 58119 return MA_INVALID_ARGS; 58120 } 58121 58122 *pLength = pAudioBufferRef->sizeInFrames; 58123 58124 return MA_SUCCESS; 58125 } 58126 58127 MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) 58128 { 58129 if (pAvailableFrames == NULL) { 58130 return MA_INVALID_ARGS; 58131 } 58132 58133 *pAvailableFrames = 0; 58134 58135 if (pAudioBufferRef == NULL) { 58136 return MA_INVALID_ARGS; 58137 } 58138 58139 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { 58140 *pAvailableFrames = 0; 58141 } else { 58142 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; 58143 } 58144 58145 return MA_SUCCESS; 58146 } 58147 58148 58149 58150 58151 MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) 58152 { 58153 ma_audio_buffer_config config; 58154 58155 MA_ZERO_OBJECT(&config); 58156 config.format = format; 58157 config.channels = channels; 58158 config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ 58159 config.sizeInFrames = sizeInFrames; 58160 config.pData = pData; 58161 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); 58162 58163 return config; 58164 } 58165 58166 static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) 58167 { 58168 ma_result result; 58169 58170 if (pAudioBuffer == NULL) { 58171 return MA_INVALID_ARGS; 58172 } 58173 58174 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ 58175 58176 if (pConfig == NULL) { 58177 return MA_INVALID_ARGS; 58178 } 58179 58180 if (pConfig->sizeInFrames == 0) { 58181 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ 58182 } 58183 58184 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); 58185 if (result != MA_SUCCESS) { 58186 return result; 58187 } 58188 58189 /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ 58190 pAudioBuffer->ref.sampleRate = pConfig->sampleRate; 58191 58192 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); 58193 58194 if (doCopy) { 58195 ma_uint64 allocationSizeInBytes; 58196 void* pData; 58197 58198 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); 58199 if (allocationSizeInBytes > MA_SIZE_MAX) { 58200 return MA_OUT_OF_MEMORY; /* Too big. */ 58201 } 58202 58203 pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ 58204 if (pData == NULL) { 58205 return MA_OUT_OF_MEMORY; 58206 } 58207 58208 if (pConfig->pData != NULL) { 58209 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); 58210 } else { 58211 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); 58212 } 58213 58214 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); 58215 pAudioBuffer->ownsData = MA_TRUE; 58216 } else { 58217 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); 58218 pAudioBuffer->ownsData = MA_FALSE; 58219 } 58220 58221 return MA_SUCCESS; 58222 } 58223 58224 static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) 58225 { 58226 if (pAudioBuffer == NULL) { 58227 return; 58228 } 58229 58230 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { 58231 ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ 58232 } 58233 58234 if (doFree) { 58235 ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); 58236 } 58237 58238 ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); 58239 } 58240 58241 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) 58242 { 58243 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); 58244 } 58245 58246 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) 58247 { 58248 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); 58249 } 58250 58251 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) 58252 { 58253 ma_result result; 58254 ma_audio_buffer* pAudioBuffer; 58255 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ 58256 ma_uint64 allocationSizeInBytes; 58257 58258 if (ppAudioBuffer == NULL) { 58259 return MA_INVALID_ARGS; 58260 } 58261 58262 *ppAudioBuffer = NULL; /* Safety. */ 58263 58264 if (pConfig == NULL) { 58265 return MA_INVALID_ARGS; 58266 } 58267 58268 innerConfig = *pConfig; 58269 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); 58270 58271 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); 58272 if (allocationSizeInBytes > MA_SIZE_MAX) { 58273 return MA_OUT_OF_MEMORY; /* Too big. */ 58274 } 58275 58276 pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ 58277 if (pAudioBuffer == NULL) { 58278 return MA_OUT_OF_MEMORY; 58279 } 58280 58281 if (pConfig->pData != NULL) { 58282 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); 58283 } else { 58284 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); 58285 } 58286 58287 innerConfig.pData = &pAudioBuffer->_pExtraData[0]; 58288 58289 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); 58290 if (result != MA_SUCCESS) { 58291 ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); 58292 return result; 58293 } 58294 58295 *ppAudioBuffer = pAudioBuffer; 58296 58297 return MA_SUCCESS; 58298 } 58299 58300 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) 58301 { 58302 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); 58303 } 58304 58305 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) 58306 { 58307 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); 58308 } 58309 58310 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) 58311 { 58312 if (pAudioBuffer == NULL) { 58313 return 0; 58314 } 58315 58316 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); 58317 } 58318 58319 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) 58320 { 58321 if (pAudioBuffer == NULL) { 58322 return MA_INVALID_ARGS; 58323 } 58324 58325 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); 58326 } 58327 58328 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) 58329 { 58330 if (ppFramesOut != NULL) { 58331 *ppFramesOut = NULL; /* Safety. */ 58332 } 58333 58334 if (pAudioBuffer == NULL) { 58335 if (pFrameCount != NULL) { 58336 *pFrameCount = 0; 58337 } 58338 58339 return MA_INVALID_ARGS; 58340 } 58341 58342 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); 58343 } 58344 58345 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) 58346 { 58347 if (pAudioBuffer == NULL) { 58348 return MA_INVALID_ARGS; 58349 } 58350 58351 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); 58352 } 58353 58354 MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) 58355 { 58356 if (pAudioBuffer == NULL) { 58357 return MA_FALSE; 58358 } 58359 58360 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); 58361 } 58362 58363 MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) 58364 { 58365 if (pAudioBuffer == NULL) { 58366 return MA_INVALID_ARGS; 58367 } 58368 58369 return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); 58370 } 58371 58372 MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) 58373 { 58374 if (pAudioBuffer == NULL) { 58375 return MA_INVALID_ARGS; 58376 } 58377 58378 return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); 58379 } 58380 58381 MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) 58382 { 58383 if (pAvailableFrames == NULL) { 58384 return MA_INVALID_ARGS; 58385 } 58386 58387 *pAvailableFrames = 0; 58388 58389 if (pAudioBuffer == NULL) { 58390 return MA_INVALID_ARGS; 58391 } 58392 58393 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); 58394 } 58395 58396 58397 58398 58399 58400 MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) 58401 { 58402 if (pData == NULL) { 58403 return MA_INVALID_ARGS; 58404 } 58405 58406 MA_ZERO_OBJECT(pData); 58407 58408 pData->format = format; 58409 pData->channels = channels; 58410 pData->pTail = &pData->head; 58411 58412 return MA_SUCCESS; 58413 } 58414 58415 MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) 58416 { 58417 ma_paged_audio_buffer_page* pPage; 58418 58419 if (pData == NULL) { 58420 return; 58421 } 58422 58423 /* All pages need to be freed. */ 58424 pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); 58425 while (pPage != NULL) { 58426 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); 58427 58428 ma_free(pPage, pAllocationCallbacks); 58429 pPage = pNext; 58430 } 58431 } 58432 58433 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) 58434 { 58435 if (pData == NULL) { 58436 return NULL; 58437 } 58438 58439 return &pData->head; 58440 } 58441 58442 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) 58443 { 58444 if (pData == NULL) { 58445 return NULL; 58446 } 58447 58448 return pData->pTail; 58449 } 58450 58451 MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) 58452 { 58453 ma_paged_audio_buffer_page* pPage; 58454 58455 if (pLength == NULL) { 58456 return MA_INVALID_ARGS; 58457 } 58458 58459 *pLength = 0; 58460 58461 if (pData == NULL) { 58462 return MA_INVALID_ARGS; 58463 } 58464 58465 /* Calculate the length from the linked list. */ 58466 for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { 58467 *pLength += pPage->sizeInFrames; 58468 } 58469 58470 return MA_SUCCESS; 58471 } 58472 58473 MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) 58474 { 58475 ma_paged_audio_buffer_page* pPage; 58476 ma_uint64 allocationSize; 58477 58478 if (ppPage == NULL) { 58479 return MA_INVALID_ARGS; 58480 } 58481 58482 *ppPage = NULL; 58483 58484 if (pData == NULL) { 58485 return MA_INVALID_ARGS; 58486 } 58487 58488 allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); 58489 if (allocationSize > MA_SIZE_MAX) { 58490 return MA_OUT_OF_MEMORY; /* Too big. */ 58491 } 58492 58493 pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ 58494 if (pPage == NULL) { 58495 return MA_OUT_OF_MEMORY; 58496 } 58497 58498 pPage->pNext = NULL; 58499 pPage->sizeInFrames = pageSizeInFrames; 58500 58501 if (pInitialData != NULL) { 58502 ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); 58503 } 58504 58505 *ppPage = pPage; 58506 58507 return MA_SUCCESS; 58508 } 58509 58510 MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) 58511 { 58512 if (pData == NULL || pPage == NULL) { 58513 return MA_INVALID_ARGS; 58514 } 58515 58516 /* It's assumed the page is not attached to the list. */ 58517 ma_free(pPage, pAllocationCallbacks); 58518 58519 return MA_SUCCESS; 58520 } 58521 58522 MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) 58523 { 58524 if (pData == NULL || pPage == NULL) { 58525 return MA_INVALID_ARGS; 58526 } 58527 58528 /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ 58529 58530 /* First thing to do is update the tail. */ 58531 for (;;) { 58532 ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); 58533 ma_paged_audio_buffer_page* pNewTail = pPage; 58534 58535 if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { 58536 /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ 58537 ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); 58538 break; /* Done. */ 58539 } 58540 } 58541 58542 return MA_SUCCESS; 58543 } 58544 58545 MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) 58546 { 58547 ma_result result; 58548 ma_paged_audio_buffer_page* pPage; 58549 58550 result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); 58551 if (result != MA_SUCCESS) { 58552 return result; 58553 } 58554 58555 return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ 58556 } 58557 58558 58559 MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) 58560 { 58561 ma_paged_audio_buffer_config config; 58562 58563 MA_ZERO_OBJECT(&config); 58564 config.pData = pData; 58565 58566 return config; 58567 } 58568 58569 58570 static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 58571 { 58572 return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); 58573 } 58574 58575 static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 58576 { 58577 return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); 58578 } 58579 58580 static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 58581 { 58582 ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; 58583 58584 *pFormat = pPagedAudioBuffer->pData->format; 58585 *pChannels = pPagedAudioBuffer->pData->channels; 58586 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ 58587 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); 58588 58589 return MA_SUCCESS; 58590 } 58591 58592 static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 58593 { 58594 return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); 58595 } 58596 58597 static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 58598 { 58599 return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); 58600 } 58601 58602 static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = 58603 { 58604 ma_paged_audio_buffer__data_source_on_read, 58605 ma_paged_audio_buffer__data_source_on_seek, 58606 ma_paged_audio_buffer__data_source_on_get_data_format, 58607 ma_paged_audio_buffer__data_source_on_get_cursor, 58608 ma_paged_audio_buffer__data_source_on_get_length, 58609 NULL, /* onSetLooping */ 58610 0 58611 }; 58612 58613 MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) 58614 { 58615 ma_result result; 58616 ma_data_source_config dataSourceConfig; 58617 58618 if (pPagedAudioBuffer == NULL) { 58619 return MA_INVALID_ARGS; 58620 } 58621 58622 MA_ZERO_OBJECT(pPagedAudioBuffer); 58623 58624 /* A config is required for the format and channel count. */ 58625 if (pConfig == NULL) { 58626 return MA_INVALID_ARGS; 58627 } 58628 58629 if (pConfig->pData == NULL) { 58630 return MA_INVALID_ARGS; /* No underlying data specified. */ 58631 } 58632 58633 dataSourceConfig = ma_data_source_config_init(); 58634 dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; 58635 58636 result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); 58637 if (result != MA_SUCCESS) { 58638 return result; 58639 } 58640 58641 pPagedAudioBuffer->pData = pConfig->pData; 58642 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); 58643 pPagedAudioBuffer->relativeCursor = 0; 58644 pPagedAudioBuffer->absoluteCursor = 0; 58645 58646 return MA_SUCCESS; 58647 } 58648 58649 MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) 58650 { 58651 if (pPagedAudioBuffer == NULL) { 58652 return; 58653 } 58654 58655 /* Nothing to do. The data needs to be deleted separately. */ 58656 } 58657 58658 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 58659 { 58660 ma_result result = MA_SUCCESS; 58661 ma_uint64 totalFramesRead = 0; 58662 ma_format format; 58663 ma_uint32 channels; 58664 58665 if (pPagedAudioBuffer == NULL) { 58666 return MA_INVALID_ARGS; 58667 } 58668 58669 format = pPagedAudioBuffer->pData->format; 58670 channels = pPagedAudioBuffer->pData->channels; 58671 58672 while (totalFramesRead < frameCount) { 58673 /* Read from the current page. The buffer should never be in a state where this is NULL. */ 58674 ma_uint64 framesRemainingInCurrentPage; 58675 ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; 58676 ma_uint64 framesToReadThisIteration; 58677 58678 MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); 58679 58680 framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; 58681 58682 framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); 58683 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); 58684 totalFramesRead += framesToReadThisIteration; 58685 58686 pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; 58687 pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; 58688 58689 /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ 58690 MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); 58691 58692 if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { 58693 /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ 58694 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); 58695 if (pNext == NULL) { 58696 result = MA_AT_END; 58697 break; /* We've reached the end. */ 58698 } else { 58699 pPagedAudioBuffer->pCurrent = pNext; 58700 pPagedAudioBuffer->relativeCursor = 0; 58701 } 58702 } 58703 } 58704 58705 if (pFramesRead != NULL) { 58706 *pFramesRead = totalFramesRead; 58707 } 58708 58709 return result; 58710 } 58711 58712 MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) 58713 { 58714 if (pPagedAudioBuffer == NULL) { 58715 return MA_INVALID_ARGS; 58716 } 58717 58718 if (frameIndex == pPagedAudioBuffer->absoluteCursor) { 58719 return MA_SUCCESS; /* Nothing to do. */ 58720 } 58721 58722 if (frameIndex < pPagedAudioBuffer->absoluteCursor) { 58723 /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ 58724 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); 58725 pPagedAudioBuffer->absoluteCursor = 0; 58726 pPagedAudioBuffer->relativeCursor = 0; 58727 58728 /* Fall through to the forward seeking section below. */ 58729 } 58730 58731 if (frameIndex > pPagedAudioBuffer->absoluteCursor) { 58732 /* Moving forward. */ 58733 ma_paged_audio_buffer_page* pPage; 58734 ma_uint64 runningCursor = 0; 58735 58736 for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { 58737 ma_uint64 pageRangeBeg = runningCursor; 58738 ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; 58739 58740 if (frameIndex >= pageRangeBeg) { 58741 if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ 58742 /* We found the page. */ 58743 pPagedAudioBuffer->pCurrent = pPage; 58744 pPagedAudioBuffer->absoluteCursor = frameIndex; 58745 pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; 58746 return MA_SUCCESS; 58747 } 58748 } 58749 58750 runningCursor = pageRangeEnd; 58751 } 58752 58753 /* Getting here means we tried seeking too far forward. Don't change any state. */ 58754 return MA_BAD_SEEK; 58755 } 58756 58757 return MA_SUCCESS; 58758 } 58759 58760 MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) 58761 { 58762 if (pCursor == NULL) { 58763 return MA_INVALID_ARGS; 58764 } 58765 58766 *pCursor = 0; /* Safety. */ 58767 58768 if (pPagedAudioBuffer == NULL) { 58769 return MA_INVALID_ARGS; 58770 } 58771 58772 *pCursor = pPagedAudioBuffer->absoluteCursor; 58773 58774 return MA_SUCCESS; 58775 } 58776 58777 MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) 58778 { 58779 return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); 58780 } 58781 58782 58783 58784 /************************************************************************************************************************************************************** 58785 58786 VFS 58787 58788 **************************************************************************************************************************************************************/ 58789 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 58790 { 58791 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58792 58793 if (pFile == NULL) { 58794 return MA_INVALID_ARGS; 58795 } 58796 58797 *pFile = NULL; 58798 58799 if (pVFS == NULL || pFilePath == NULL || openMode == 0) { 58800 return MA_INVALID_ARGS; 58801 } 58802 58803 if (pCallbacks->onOpen == NULL) { 58804 return MA_NOT_IMPLEMENTED; 58805 } 58806 58807 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); 58808 } 58809 58810 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 58811 { 58812 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58813 58814 if (pFile == NULL) { 58815 return MA_INVALID_ARGS; 58816 } 58817 58818 *pFile = NULL; 58819 58820 if (pVFS == NULL || pFilePath == NULL || openMode == 0) { 58821 return MA_INVALID_ARGS; 58822 } 58823 58824 if (pCallbacks->onOpenW == NULL) { 58825 return MA_NOT_IMPLEMENTED; 58826 } 58827 58828 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); 58829 } 58830 58831 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) 58832 { 58833 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58834 58835 if (pVFS == NULL || file == NULL) { 58836 return MA_INVALID_ARGS; 58837 } 58838 58839 if (pCallbacks->onClose == NULL) { 58840 return MA_NOT_IMPLEMENTED; 58841 } 58842 58843 return pCallbacks->onClose(pVFS, file); 58844 } 58845 58846 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) 58847 { 58848 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58849 ma_result result; 58850 size_t bytesRead = 0; 58851 58852 if (pBytesRead != NULL) { 58853 *pBytesRead = 0; 58854 } 58855 58856 if (pVFS == NULL || file == NULL || pDst == NULL) { 58857 return MA_INVALID_ARGS; 58858 } 58859 58860 if (pCallbacks->onRead == NULL) { 58861 return MA_NOT_IMPLEMENTED; 58862 } 58863 58864 result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); 58865 58866 if (pBytesRead != NULL) { 58867 *pBytesRead = bytesRead; 58868 } 58869 58870 if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { 58871 result = MA_AT_END; 58872 } 58873 58874 return result; 58875 } 58876 58877 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) 58878 { 58879 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58880 58881 if (pBytesWritten != NULL) { 58882 *pBytesWritten = 0; 58883 } 58884 58885 if (pVFS == NULL || file == NULL || pSrc == NULL) { 58886 return MA_INVALID_ARGS; 58887 } 58888 58889 if (pCallbacks->onWrite == NULL) { 58890 return MA_NOT_IMPLEMENTED; 58891 } 58892 58893 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); 58894 } 58895 58896 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) 58897 { 58898 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58899 58900 if (pVFS == NULL || file == NULL) { 58901 return MA_INVALID_ARGS; 58902 } 58903 58904 if (pCallbacks->onSeek == NULL) { 58905 return MA_NOT_IMPLEMENTED; 58906 } 58907 58908 return pCallbacks->onSeek(pVFS, file, offset, origin); 58909 } 58910 58911 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) 58912 { 58913 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58914 58915 if (pCursor == NULL) { 58916 return MA_INVALID_ARGS; 58917 } 58918 58919 *pCursor = 0; 58920 58921 if (pVFS == NULL || file == NULL) { 58922 return MA_INVALID_ARGS; 58923 } 58924 58925 if (pCallbacks->onTell == NULL) { 58926 return MA_NOT_IMPLEMENTED; 58927 } 58928 58929 return pCallbacks->onTell(pVFS, file, pCursor); 58930 } 58931 58932 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) 58933 { 58934 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; 58935 58936 if (pInfo == NULL) { 58937 return MA_INVALID_ARGS; 58938 } 58939 58940 MA_ZERO_OBJECT(pInfo); 58941 58942 if (pVFS == NULL || file == NULL) { 58943 return MA_INVALID_ARGS; 58944 } 58945 58946 if (pCallbacks->onInfo == NULL) { 58947 return MA_NOT_IMPLEMENTED; 58948 } 58949 58950 return pCallbacks->onInfo(pVFS, file, pInfo); 58951 } 58952 58953 58954 #if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) 58955 #define MA_USE_WIN32_FILEIO 58956 #endif 58957 58958 #if defined(MA_USE_WIN32_FILEIO) 58959 /* 58960 We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do 58961 not have the Ex version. We therefore need to do some dynamic branching depending on what's available. 58962 58963 We load these when we load our first file from the default VFS. It's left open for the life of the 58964 program and is left to the OS to uninitialize when the program terminates. 58965 */ 58966 typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); 58967 typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); 58968 58969 static ma_handle hKernel32DLL = NULL; 58970 static ma_SetFilePointer_proc ma_SetFilePointer = NULL; 58971 static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; 58972 58973 static void ma_win32_fileio_init(void) 58974 { 58975 if (hKernel32DLL == NULL) { 58976 hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); 58977 if (hKernel32DLL != NULL) { 58978 ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); 58979 ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); 58980 } 58981 } 58982 } 58983 58984 static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) 58985 { 58986 *pDesiredAccess = 0; 58987 if ((openMode & MA_OPEN_MODE_READ) != 0) { 58988 *pDesiredAccess |= GENERIC_READ; 58989 } 58990 if ((openMode & MA_OPEN_MODE_WRITE) != 0) { 58991 *pDesiredAccess |= GENERIC_WRITE; 58992 } 58993 58994 *pShareMode = 0; 58995 if ((openMode & MA_OPEN_MODE_READ) != 0) { 58996 *pShareMode |= FILE_SHARE_READ; 58997 } 58998 58999 if ((openMode & MA_OPEN_MODE_WRITE) != 0) { 59000 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ 59001 } else { 59002 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ 59003 } 59004 } 59005 59006 static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59007 { 59008 HANDLE hFile; 59009 DWORD dwDesiredAccess; 59010 DWORD dwShareMode; 59011 DWORD dwCreationDisposition; 59012 59013 (void)pVFS; 59014 59015 /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ 59016 ma_win32_fileio_init(); 59017 59018 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); 59019 59020 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); 59021 if (hFile == INVALID_HANDLE_VALUE) { 59022 return ma_result_from_GetLastError(GetLastError()); 59023 } 59024 59025 *pFile = hFile; 59026 return MA_SUCCESS; 59027 } 59028 59029 static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59030 { 59031 HANDLE hFile; 59032 DWORD dwDesiredAccess; 59033 DWORD dwShareMode; 59034 DWORD dwCreationDisposition; 59035 59036 (void)pVFS; 59037 59038 /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ 59039 ma_win32_fileio_init(); 59040 59041 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); 59042 59043 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); 59044 if (hFile == INVALID_HANDLE_VALUE) { 59045 return ma_result_from_GetLastError(GetLastError()); 59046 } 59047 59048 *pFile = hFile; 59049 return MA_SUCCESS; 59050 } 59051 59052 static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) 59053 { 59054 (void)pVFS; 59055 59056 if (CloseHandle((HANDLE)file) == 0) { 59057 return ma_result_from_GetLastError(GetLastError()); 59058 } 59059 59060 return MA_SUCCESS; 59061 } 59062 59063 59064 static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) 59065 { 59066 ma_result result = MA_SUCCESS; 59067 size_t totalBytesRead; 59068 59069 (void)pVFS; 59070 59071 totalBytesRead = 0; 59072 while (totalBytesRead < sizeInBytes) { 59073 size_t bytesRemaining; 59074 DWORD bytesToRead; 59075 DWORD bytesRead; 59076 BOOL readResult; 59077 59078 bytesRemaining = sizeInBytes - totalBytesRead; 59079 if (bytesRemaining >= 0xFFFFFFFF) { 59080 bytesToRead = 0xFFFFFFFF; 59081 } else { 59082 bytesToRead = (DWORD)bytesRemaining; 59083 } 59084 59085 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); 59086 if (readResult == 1 && bytesRead == 0) { 59087 result = MA_AT_END; 59088 break; /* EOF */ 59089 } 59090 59091 totalBytesRead += bytesRead; 59092 59093 if (bytesRead < bytesToRead) { 59094 break; /* EOF */ 59095 } 59096 59097 if (readResult == 0) { 59098 result = ma_result_from_GetLastError(GetLastError()); 59099 break; 59100 } 59101 } 59102 59103 if (pBytesRead != NULL) { 59104 *pBytesRead = totalBytesRead; 59105 } 59106 59107 return result; 59108 } 59109 59110 static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) 59111 { 59112 ma_result result = MA_SUCCESS; 59113 size_t totalBytesWritten; 59114 59115 (void)pVFS; 59116 59117 totalBytesWritten = 0; 59118 while (totalBytesWritten < sizeInBytes) { 59119 size_t bytesRemaining; 59120 DWORD bytesToWrite; 59121 DWORD bytesWritten; 59122 BOOL writeResult; 59123 59124 bytesRemaining = sizeInBytes - totalBytesWritten; 59125 if (bytesRemaining >= 0xFFFFFFFF) { 59126 bytesToWrite = 0xFFFFFFFF; 59127 } else { 59128 bytesToWrite = (DWORD)bytesRemaining; 59129 } 59130 59131 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); 59132 totalBytesWritten += bytesWritten; 59133 59134 if (writeResult == 0) { 59135 result = ma_result_from_GetLastError(GetLastError()); 59136 break; 59137 } 59138 } 59139 59140 if (pBytesWritten != NULL) { 59141 *pBytesWritten = totalBytesWritten; 59142 } 59143 59144 return result; 59145 } 59146 59147 59148 static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) 59149 { 59150 LARGE_INTEGER liDistanceToMove; 59151 DWORD dwMoveMethod; 59152 BOOL result; 59153 59154 (void)pVFS; 59155 59156 liDistanceToMove.QuadPart = offset; 59157 59158 /* */ if (origin == ma_seek_origin_current) { 59159 dwMoveMethod = FILE_CURRENT; 59160 } else if (origin == ma_seek_origin_end) { 59161 dwMoveMethod = FILE_END; 59162 } else { 59163 dwMoveMethod = FILE_BEGIN; 59164 } 59165 59166 if (ma_SetFilePointerEx != NULL) { 59167 result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); 59168 } else if (ma_SetFilePointer != NULL) { 59169 /* No SetFilePointerEx() so restrict to 31 bits. */ 59170 if (origin > 0x7FFFFFFF) { 59171 return MA_OUT_OF_RANGE; 59172 } 59173 59174 result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); 59175 } else { 59176 return MA_NOT_IMPLEMENTED; 59177 } 59178 59179 if (result == 0) { 59180 return ma_result_from_GetLastError(GetLastError()); 59181 } 59182 59183 return MA_SUCCESS; 59184 } 59185 59186 static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) 59187 { 59188 LARGE_INTEGER liZero; 59189 LARGE_INTEGER liTell; 59190 BOOL result; 59191 59192 (void)pVFS; 59193 59194 liZero.QuadPart = 0; 59195 59196 if (ma_SetFilePointerEx != NULL) { 59197 result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); 59198 } else if (ma_SetFilePointer != NULL) { 59199 LONG tell; 59200 59201 result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); 59202 liTell.QuadPart = tell; 59203 } else { 59204 return MA_NOT_IMPLEMENTED; 59205 } 59206 59207 if (result == 0) { 59208 return ma_result_from_GetLastError(GetLastError()); 59209 } 59210 59211 if (pCursor != NULL) { 59212 *pCursor = liTell.QuadPart; 59213 } 59214 59215 return MA_SUCCESS; 59216 } 59217 59218 static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) 59219 { 59220 BY_HANDLE_FILE_INFORMATION fi; 59221 BOOL result; 59222 59223 (void)pVFS; 59224 59225 result = GetFileInformationByHandle((HANDLE)file, &fi); 59226 if (result == 0) { 59227 return ma_result_from_GetLastError(GetLastError()); 59228 } 59229 59230 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); 59231 59232 return MA_SUCCESS; 59233 } 59234 #else 59235 static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59236 { 59237 ma_result result; 59238 FILE* pFileStd; 59239 const char* pOpenModeStr; 59240 59241 MA_ASSERT(pFilePath != NULL); 59242 MA_ASSERT(openMode != 0); 59243 MA_ASSERT(pFile != NULL); 59244 59245 (void)pVFS; 59246 59247 if ((openMode & MA_OPEN_MODE_READ) != 0) { 59248 if ((openMode & MA_OPEN_MODE_WRITE) != 0) { 59249 pOpenModeStr = "r+"; 59250 } else { 59251 pOpenModeStr = "rb"; 59252 } 59253 } else { 59254 pOpenModeStr = "wb"; 59255 } 59256 59257 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); 59258 if (result != MA_SUCCESS) { 59259 return result; 59260 } 59261 59262 *pFile = pFileStd; 59263 59264 return MA_SUCCESS; 59265 } 59266 59267 static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59268 { 59269 ma_result result; 59270 FILE* pFileStd; 59271 const wchar_t* pOpenModeStr; 59272 59273 MA_ASSERT(pFilePath != NULL); 59274 MA_ASSERT(openMode != 0); 59275 MA_ASSERT(pFile != NULL); 59276 59277 (void)pVFS; 59278 59279 if ((openMode & MA_OPEN_MODE_READ) != 0) { 59280 if ((openMode & MA_OPEN_MODE_WRITE) != 0) { 59281 pOpenModeStr = L"r+"; 59282 } else { 59283 pOpenModeStr = L"rb"; 59284 } 59285 } else { 59286 pOpenModeStr = L"wb"; 59287 } 59288 59289 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); 59290 if (result != MA_SUCCESS) { 59291 return result; 59292 } 59293 59294 *pFile = pFileStd; 59295 59296 return MA_SUCCESS; 59297 } 59298 59299 static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) 59300 { 59301 MA_ASSERT(file != NULL); 59302 59303 (void)pVFS; 59304 59305 fclose((FILE*)file); 59306 59307 return MA_SUCCESS; 59308 } 59309 59310 static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) 59311 { 59312 size_t result; 59313 59314 MA_ASSERT(file != NULL); 59315 MA_ASSERT(pDst != NULL); 59316 59317 (void)pVFS; 59318 59319 result = fread(pDst, 1, sizeInBytes, (FILE*)file); 59320 59321 if (pBytesRead != NULL) { 59322 *pBytesRead = result; 59323 } 59324 59325 if (result != sizeInBytes) { 59326 if (result == 0 && feof((FILE*)file)) { 59327 return MA_AT_END; 59328 } else { 59329 return ma_result_from_errno(ferror((FILE*)file)); 59330 } 59331 } 59332 59333 return MA_SUCCESS; 59334 } 59335 59336 static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) 59337 { 59338 size_t result; 59339 59340 MA_ASSERT(file != NULL); 59341 MA_ASSERT(pSrc != NULL); 59342 59343 (void)pVFS; 59344 59345 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); 59346 59347 if (pBytesWritten != NULL) { 59348 *pBytesWritten = result; 59349 } 59350 59351 if (result != sizeInBytes) { 59352 return ma_result_from_errno(ferror((FILE*)file)); 59353 } 59354 59355 return MA_SUCCESS; 59356 } 59357 59358 static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) 59359 { 59360 int result; 59361 int whence; 59362 59363 MA_ASSERT(file != NULL); 59364 59365 (void)pVFS; 59366 59367 if (origin == ma_seek_origin_start) { 59368 whence = SEEK_SET; 59369 } else if (origin == ma_seek_origin_end) { 59370 whence = SEEK_END; 59371 } else { 59372 whence = SEEK_CUR; 59373 } 59374 59375 #if defined(_WIN32) 59376 #if defined(_MSC_VER) && _MSC_VER > 1200 59377 result = _fseeki64((FILE*)file, offset, whence); 59378 #else 59379 /* No _fseeki64() so restrict to 31 bits. */ 59380 if (origin > 0x7FFFFFFF) { 59381 return MA_OUT_OF_RANGE; 59382 } 59383 59384 result = fseek((FILE*)file, (int)offset, whence); 59385 #endif 59386 #else 59387 result = fseek((FILE*)file, (long int)offset, whence); 59388 #endif 59389 if (result != 0) { 59390 return MA_ERROR; 59391 } 59392 59393 return MA_SUCCESS; 59394 } 59395 59396 static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) 59397 { 59398 ma_int64 result; 59399 59400 MA_ASSERT(file != NULL); 59401 MA_ASSERT(pCursor != NULL); 59402 59403 (void)pVFS; 59404 59405 #if defined(_WIN32) 59406 #if defined(_MSC_VER) && _MSC_VER > 1200 59407 result = _ftelli64((FILE*)file); 59408 #else 59409 result = ftell((FILE*)file); 59410 #endif 59411 #else 59412 result = ftell((FILE*)file); 59413 #endif 59414 59415 *pCursor = result; 59416 59417 return MA_SUCCESS; 59418 } 59419 59420 #if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) 59421 int fileno(FILE *stream); 59422 #endif 59423 59424 static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) 59425 { 59426 int fd; 59427 struct stat info; 59428 59429 MA_ASSERT(file != NULL); 59430 MA_ASSERT(pInfo != NULL); 59431 59432 (void)pVFS; 59433 59434 #if defined(_MSC_VER) 59435 fd = _fileno((FILE*)file); 59436 #else 59437 fd = fileno((FILE*)file); 59438 #endif 59439 59440 if (fstat(fd, &info) != 0) { 59441 return ma_result_from_errno(errno); 59442 } 59443 59444 pInfo->sizeInBytes = info.st_size; 59445 59446 return MA_SUCCESS; 59447 } 59448 #endif 59449 59450 59451 static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59452 { 59453 if (pFile == NULL) { 59454 return MA_INVALID_ARGS; 59455 } 59456 59457 *pFile = NULL; 59458 59459 if (pFilePath == NULL || openMode == 0) { 59460 return MA_INVALID_ARGS; 59461 } 59462 59463 #if defined(MA_USE_WIN32_FILEIO) 59464 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); 59465 #else 59466 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); 59467 #endif 59468 } 59469 59470 static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59471 { 59472 if (pFile == NULL) { 59473 return MA_INVALID_ARGS; 59474 } 59475 59476 *pFile = NULL; 59477 59478 if (pFilePath == NULL || openMode == 0) { 59479 return MA_INVALID_ARGS; 59480 } 59481 59482 #if defined(MA_USE_WIN32_FILEIO) 59483 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); 59484 #else 59485 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); 59486 #endif 59487 } 59488 59489 static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) 59490 { 59491 if (file == NULL) { 59492 return MA_INVALID_ARGS; 59493 } 59494 59495 #if defined(MA_USE_WIN32_FILEIO) 59496 return ma_default_vfs_close__win32(pVFS, file); 59497 #else 59498 return ma_default_vfs_close__stdio(pVFS, file); 59499 #endif 59500 } 59501 59502 static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) 59503 { 59504 if (pBytesRead != NULL) { 59505 *pBytesRead = 0; 59506 } 59507 59508 if (file == NULL || pDst == NULL) { 59509 return MA_INVALID_ARGS; 59510 } 59511 59512 #if defined(MA_USE_WIN32_FILEIO) 59513 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); 59514 #else 59515 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); 59516 #endif 59517 } 59518 59519 static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) 59520 { 59521 if (pBytesWritten != NULL) { 59522 *pBytesWritten = 0; 59523 } 59524 59525 if (file == NULL || pSrc == NULL) { 59526 return MA_INVALID_ARGS; 59527 } 59528 59529 #if defined(MA_USE_WIN32_FILEIO) 59530 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); 59531 #else 59532 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); 59533 #endif 59534 } 59535 59536 static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) 59537 { 59538 if (file == NULL) { 59539 return MA_INVALID_ARGS; 59540 } 59541 59542 #if defined(MA_USE_WIN32_FILEIO) 59543 return ma_default_vfs_seek__win32(pVFS, file, offset, origin); 59544 #else 59545 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); 59546 #endif 59547 } 59548 59549 static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) 59550 { 59551 if (pCursor == NULL) { 59552 return MA_INVALID_ARGS; 59553 } 59554 59555 *pCursor = 0; 59556 59557 if (file == NULL) { 59558 return MA_INVALID_ARGS; 59559 } 59560 59561 #if defined(MA_USE_WIN32_FILEIO) 59562 return ma_default_vfs_tell__win32(pVFS, file, pCursor); 59563 #else 59564 return ma_default_vfs_tell__stdio(pVFS, file, pCursor); 59565 #endif 59566 } 59567 59568 static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) 59569 { 59570 if (pInfo == NULL) { 59571 return MA_INVALID_ARGS; 59572 } 59573 59574 MA_ZERO_OBJECT(pInfo); 59575 59576 if (file == NULL) { 59577 return MA_INVALID_ARGS; 59578 } 59579 59580 #if defined(MA_USE_WIN32_FILEIO) 59581 return ma_default_vfs_info__win32(pVFS, file, pInfo); 59582 #else 59583 return ma_default_vfs_info__stdio(pVFS, file, pInfo); 59584 #endif 59585 } 59586 59587 59588 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) 59589 { 59590 if (pVFS == NULL) { 59591 return MA_INVALID_ARGS; 59592 } 59593 59594 pVFS->cb.onOpen = ma_default_vfs_open; 59595 pVFS->cb.onOpenW = ma_default_vfs_open_w; 59596 pVFS->cb.onClose = ma_default_vfs_close; 59597 pVFS->cb.onRead = ma_default_vfs_read; 59598 pVFS->cb.onWrite = ma_default_vfs_write; 59599 pVFS->cb.onSeek = ma_default_vfs_seek; 59600 pVFS->cb.onTell = ma_default_vfs_tell; 59601 pVFS->cb.onInfo = ma_default_vfs_info; 59602 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); 59603 59604 return MA_SUCCESS; 59605 } 59606 59607 59608 MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59609 { 59610 if (pVFS != NULL) { 59611 return ma_vfs_open(pVFS, pFilePath, openMode, pFile); 59612 } else { 59613 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); 59614 } 59615 } 59616 59617 MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) 59618 { 59619 if (pVFS != NULL) { 59620 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); 59621 } else { 59622 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); 59623 } 59624 } 59625 59626 MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) 59627 { 59628 if (pVFS != NULL) { 59629 return ma_vfs_close(pVFS, file); 59630 } else { 59631 return ma_default_vfs_close(pVFS, file); 59632 } 59633 } 59634 59635 MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) 59636 { 59637 if (pVFS != NULL) { 59638 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); 59639 } else { 59640 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); 59641 } 59642 } 59643 59644 MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) 59645 { 59646 if (pVFS != NULL) { 59647 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); 59648 } else { 59649 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); 59650 } 59651 } 59652 59653 MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) 59654 { 59655 if (pVFS != NULL) { 59656 return ma_vfs_seek(pVFS, file, offset, origin); 59657 } else { 59658 return ma_default_vfs_seek(pVFS, file, offset, origin); 59659 } 59660 } 59661 59662 MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) 59663 { 59664 if (pVFS != NULL) { 59665 return ma_vfs_tell(pVFS, file, pCursor); 59666 } else { 59667 return ma_default_vfs_tell(pVFS, file, pCursor); 59668 } 59669 } 59670 59671 MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) 59672 { 59673 if (pVFS != NULL) { 59674 return ma_vfs_info(pVFS, file, pInfo); 59675 } else { 59676 return ma_default_vfs_info(pVFS, file, pInfo); 59677 } 59678 } 59679 59680 59681 59682 static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) 59683 { 59684 ma_result result; 59685 ma_vfs_file file; 59686 ma_file_info info; 59687 void* pData; 59688 size_t bytesRead; 59689 59690 if (ppData != NULL) { 59691 *ppData = NULL; 59692 } 59693 if (pSize != NULL) { 59694 *pSize = 0; 59695 } 59696 59697 if (ppData == NULL) { 59698 return MA_INVALID_ARGS; 59699 } 59700 59701 if (pFilePath != NULL) { 59702 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); 59703 } else { 59704 result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); 59705 } 59706 if (result != MA_SUCCESS) { 59707 return result; 59708 } 59709 59710 result = ma_vfs_or_default_info(pVFS, file, &info); 59711 if (result != MA_SUCCESS) { 59712 ma_vfs_or_default_close(pVFS, file); 59713 return result; 59714 } 59715 59716 if (info.sizeInBytes > MA_SIZE_MAX) { 59717 ma_vfs_or_default_close(pVFS, file); 59718 return MA_TOO_BIG; 59719 } 59720 59721 pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ 59722 if (pData == NULL) { 59723 ma_vfs_or_default_close(pVFS, file); 59724 return result; 59725 } 59726 59727 result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ 59728 ma_vfs_or_default_close(pVFS, file); 59729 59730 if (result != MA_SUCCESS) { 59731 ma_free(pData, pAllocationCallbacks); 59732 return result; 59733 } 59734 59735 if (pSize != NULL) { 59736 *pSize = bytesRead; 59737 } 59738 59739 MA_ASSERT(ppData != NULL); 59740 *ppData = pData; 59741 59742 return MA_SUCCESS; 59743 } 59744 59745 MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) 59746 { 59747 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); 59748 } 59749 59750 MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) 59751 { 59752 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); 59753 } 59754 59755 59756 59757 /************************************************************************************************************************************************************** 59758 59759 Decoding and Encoding Headers. These are auto-generated from a tool. 59760 59761 **************************************************************************************************************************************************************/ 59762 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) 59763 /* dr_wav_h begin */ 59764 #ifndef ma_dr_wav_h 59765 #define ma_dr_wav_h 59766 #ifdef __cplusplus 59767 extern "C" { 59768 #endif 59769 #define MA_DR_WAV_STRINGIFY(x) #x 59770 #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) 59771 #define MA_DR_WAV_VERSION_MAJOR 0 59772 #define MA_DR_WAV_VERSION_MINOR 13 59773 #define MA_DR_WAV_VERSION_REVISION 13 59774 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) 59775 #include <stddef.h> 59776 #define MA_DR_WAVE_FORMAT_PCM 0x1 59777 #define MA_DR_WAVE_FORMAT_ADPCM 0x2 59778 #define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 59779 #define MA_DR_WAVE_FORMAT_ALAW 0x6 59780 #define MA_DR_WAVE_FORMAT_MULAW 0x7 59781 #define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 59782 #define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE 59783 #define MA_DR_WAV_SEQUENTIAL 0x00000001 59784 #define MA_DR_WAV_WITH_METADATA 0x00000002 59785 MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); 59786 MA_API const char* ma_dr_wav_version_string(void); 59787 typedef enum 59788 { 59789 ma_dr_wav_seek_origin_start, 59790 ma_dr_wav_seek_origin_current 59791 } ma_dr_wav_seek_origin; 59792 typedef enum 59793 { 59794 ma_dr_wav_container_riff, 59795 ma_dr_wav_container_rifx, 59796 ma_dr_wav_container_w64, 59797 ma_dr_wav_container_rf64, 59798 ma_dr_wav_container_aiff 59799 } ma_dr_wav_container; 59800 typedef struct 59801 { 59802 union 59803 { 59804 ma_uint8 fourcc[4]; 59805 ma_uint8 guid[16]; 59806 } id; 59807 ma_uint64 sizeInBytes; 59808 unsigned int paddingSize; 59809 } ma_dr_wav_chunk_header; 59810 typedef struct 59811 { 59812 ma_uint16 formatTag; 59813 ma_uint16 channels; 59814 ma_uint32 sampleRate; 59815 ma_uint32 avgBytesPerSec; 59816 ma_uint16 blockAlign; 59817 ma_uint16 bitsPerSample; 59818 ma_uint16 extendedSize; 59819 ma_uint16 validBitsPerSample; 59820 ma_uint32 channelMask; 59821 ma_uint8 subFormat[16]; 59822 } ma_dr_wav_fmt; 59823 MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); 59824 typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); 59825 typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); 59826 typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); 59827 typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); 59828 typedef struct 59829 { 59830 const ma_uint8* data; 59831 size_t dataSize; 59832 size_t currentReadPos; 59833 } ma_dr_wav__memory_stream; 59834 typedef struct 59835 { 59836 void** ppData; 59837 size_t* pDataSize; 59838 size_t dataSize; 59839 size_t dataCapacity; 59840 size_t currentWritePos; 59841 } ma_dr_wav__memory_stream_write; 59842 typedef struct 59843 { 59844 ma_dr_wav_container container; 59845 ma_uint32 format; 59846 ma_uint32 channels; 59847 ma_uint32 sampleRate; 59848 ma_uint32 bitsPerSample; 59849 } ma_dr_wav_data_format; 59850 typedef enum 59851 { 59852 ma_dr_wav_metadata_type_none = 0, 59853 ma_dr_wav_metadata_type_unknown = 1 << 0, 59854 ma_dr_wav_metadata_type_smpl = 1 << 1, 59855 ma_dr_wav_metadata_type_inst = 1 << 2, 59856 ma_dr_wav_metadata_type_cue = 1 << 3, 59857 ma_dr_wav_metadata_type_acid = 1 << 4, 59858 ma_dr_wav_metadata_type_bext = 1 << 5, 59859 ma_dr_wav_metadata_type_list_label = 1 << 6, 59860 ma_dr_wav_metadata_type_list_note = 1 << 7, 59861 ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, 59862 ma_dr_wav_metadata_type_list_info_software = 1 << 9, 59863 ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, 59864 ma_dr_wav_metadata_type_list_info_title = 1 << 11, 59865 ma_dr_wav_metadata_type_list_info_artist = 1 << 12, 59866 ma_dr_wav_metadata_type_list_info_comment = 1 << 13, 59867 ma_dr_wav_metadata_type_list_info_date = 1 << 14, 59868 ma_dr_wav_metadata_type_list_info_genre = 1 << 15, 59869 ma_dr_wav_metadata_type_list_info_album = 1 << 16, 59870 ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, 59871 ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software 59872 | ma_dr_wav_metadata_type_list_info_copyright 59873 | ma_dr_wav_metadata_type_list_info_title 59874 | ma_dr_wav_metadata_type_list_info_artist 59875 | ma_dr_wav_metadata_type_list_info_comment 59876 | ma_dr_wav_metadata_type_list_info_date 59877 | ma_dr_wav_metadata_type_list_info_genre 59878 | ma_dr_wav_metadata_type_list_info_album 59879 | ma_dr_wav_metadata_type_list_info_tracknumber, 59880 ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label 59881 | ma_dr_wav_metadata_type_list_note 59882 | ma_dr_wav_metadata_type_list_labelled_cue_region, 59883 ma_dr_wav_metadata_type_all = -2, 59884 ma_dr_wav_metadata_type_all_including_unknown = -1 59885 } ma_dr_wav_metadata_type; 59886 typedef enum 59887 { 59888 ma_dr_wav_smpl_loop_type_forward = 0, 59889 ma_dr_wav_smpl_loop_type_pingpong = 1, 59890 ma_dr_wav_smpl_loop_type_backward = 2 59891 } ma_dr_wav_smpl_loop_type; 59892 typedef struct 59893 { 59894 ma_uint32 cuePointId; 59895 ma_uint32 type; 59896 ma_uint32 firstSampleByteOffset; 59897 ma_uint32 lastSampleByteOffset; 59898 ma_uint32 sampleFraction; 59899 ma_uint32 playCount; 59900 } ma_dr_wav_smpl_loop; 59901 typedef struct 59902 { 59903 ma_uint32 manufacturerId; 59904 ma_uint32 productId; 59905 ma_uint32 samplePeriodNanoseconds; 59906 ma_uint32 midiUnityNote; 59907 ma_uint32 midiPitchFraction; 59908 ma_uint32 smpteFormat; 59909 ma_uint32 smpteOffset; 59910 ma_uint32 sampleLoopCount; 59911 ma_uint32 samplerSpecificDataSizeInBytes; 59912 ma_dr_wav_smpl_loop* pLoops; 59913 ma_uint8* pSamplerSpecificData; 59914 } ma_dr_wav_smpl; 59915 typedef struct 59916 { 59917 ma_int8 midiUnityNote; 59918 ma_int8 fineTuneCents; 59919 ma_int8 gainDecibels; 59920 ma_int8 lowNote; 59921 ma_int8 highNote; 59922 ma_int8 lowVelocity; 59923 ma_int8 highVelocity; 59924 } ma_dr_wav_inst; 59925 typedef struct 59926 { 59927 ma_uint32 id; 59928 ma_uint32 playOrderPosition; 59929 ma_uint8 dataChunkId[4]; 59930 ma_uint32 chunkStart; 59931 ma_uint32 blockStart; 59932 ma_uint32 sampleByteOffset; 59933 } ma_dr_wav_cue_point; 59934 typedef struct 59935 { 59936 ma_uint32 cuePointCount; 59937 ma_dr_wav_cue_point *pCuePoints; 59938 } ma_dr_wav_cue; 59939 typedef enum 59940 { 59941 ma_dr_wav_acid_flag_one_shot = 1, 59942 ma_dr_wav_acid_flag_root_note_set = 2, 59943 ma_dr_wav_acid_flag_stretch = 4, 59944 ma_dr_wav_acid_flag_disk_based = 8, 59945 ma_dr_wav_acid_flag_acidizer = 16 59946 } ma_dr_wav_acid_flag; 59947 typedef struct 59948 { 59949 ma_uint32 flags; 59950 ma_uint16 midiUnityNote; 59951 ma_uint16 reserved1; 59952 float reserved2; 59953 ma_uint32 numBeats; 59954 ma_uint16 meterDenominator; 59955 ma_uint16 meterNumerator; 59956 float tempo; 59957 } ma_dr_wav_acid; 59958 typedef struct 59959 { 59960 ma_uint32 cuePointId; 59961 ma_uint32 stringLength; 59962 char* pString; 59963 } ma_dr_wav_list_label_or_note; 59964 typedef struct 59965 { 59966 char* pDescription; 59967 char* pOriginatorName; 59968 char* pOriginatorReference; 59969 char pOriginationDate[10]; 59970 char pOriginationTime[8]; 59971 ma_uint64 timeReference; 59972 ma_uint16 version; 59973 char* pCodingHistory; 59974 ma_uint32 codingHistorySize; 59975 ma_uint8* pUMID; 59976 ma_uint16 loudnessValue; 59977 ma_uint16 loudnessRange; 59978 ma_uint16 maxTruePeakLevel; 59979 ma_uint16 maxMomentaryLoudness; 59980 ma_uint16 maxShortTermLoudness; 59981 } ma_dr_wav_bext; 59982 typedef struct 59983 { 59984 ma_uint32 stringLength; 59985 char* pString; 59986 } ma_dr_wav_list_info_text; 59987 typedef struct 59988 { 59989 ma_uint32 cuePointId; 59990 ma_uint32 sampleLength; 59991 ma_uint8 purposeId[4]; 59992 ma_uint16 country; 59993 ma_uint16 language; 59994 ma_uint16 dialect; 59995 ma_uint16 codePage; 59996 ma_uint32 stringLength; 59997 char* pString; 59998 } ma_dr_wav_list_labelled_cue_region; 59999 typedef enum 60000 { 60001 ma_dr_wav_metadata_location_invalid, 60002 ma_dr_wav_metadata_location_top_level, 60003 ma_dr_wav_metadata_location_inside_info_list, 60004 ma_dr_wav_metadata_location_inside_adtl_list 60005 } ma_dr_wav_metadata_location; 60006 typedef struct 60007 { 60008 ma_uint8 id[4]; 60009 ma_dr_wav_metadata_location chunkLocation; 60010 ma_uint32 dataSizeInBytes; 60011 ma_uint8* pData; 60012 } ma_dr_wav_unknown_metadata; 60013 typedef struct 60014 { 60015 ma_dr_wav_metadata_type type; 60016 union 60017 { 60018 ma_dr_wav_cue cue; 60019 ma_dr_wav_smpl smpl; 60020 ma_dr_wav_acid acid; 60021 ma_dr_wav_inst inst; 60022 ma_dr_wav_bext bext; 60023 ma_dr_wav_list_label_or_note labelOrNote; 60024 ma_dr_wav_list_labelled_cue_region labelledCueRegion; 60025 ma_dr_wav_list_info_text infoText; 60026 ma_dr_wav_unknown_metadata unknown; 60027 } data; 60028 } ma_dr_wav_metadata; 60029 typedef struct 60030 { 60031 ma_dr_wav_read_proc onRead; 60032 ma_dr_wav_write_proc onWrite; 60033 ma_dr_wav_seek_proc onSeek; 60034 void* pUserData; 60035 ma_allocation_callbacks allocationCallbacks; 60036 ma_dr_wav_container container; 60037 ma_dr_wav_fmt fmt; 60038 ma_uint32 sampleRate; 60039 ma_uint16 channels; 60040 ma_uint16 bitsPerSample; 60041 ma_uint16 translatedFormatTag; 60042 ma_uint64 totalPCMFrameCount; 60043 ma_uint64 dataChunkDataSize; 60044 ma_uint64 dataChunkDataPos; 60045 ma_uint64 bytesRemaining; 60046 ma_uint64 readCursorInPCMFrames; 60047 ma_uint64 dataChunkDataSizeTargetWrite; 60048 ma_bool32 isSequentialWrite; 60049 ma_dr_wav_metadata* pMetadata; 60050 ma_uint32 metadataCount; 60051 ma_dr_wav__memory_stream memoryStream; 60052 ma_dr_wav__memory_stream_write memoryStreamWrite; 60053 struct 60054 { 60055 ma_uint32 bytesRemainingInBlock; 60056 ma_uint16 predictor[2]; 60057 ma_int32 delta[2]; 60058 ma_int32 cachedFrames[4]; 60059 ma_uint32 cachedFrameCount; 60060 ma_int32 prevFrames[2][2]; 60061 } msadpcm; 60062 struct 60063 { 60064 ma_uint32 bytesRemainingInBlock; 60065 ma_int32 predictor[2]; 60066 ma_int32 stepIndex[2]; 60067 ma_int32 cachedFrames[16]; 60068 ma_uint32 cachedFrameCount; 60069 } ima; 60070 struct 60071 { 60072 ma_bool8 isLE; 60073 ma_bool8 isUnsigned; 60074 } aiff; 60075 } ma_dr_wav; 60076 MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60077 MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60078 MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60079 MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60080 MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60081 MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60082 MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); 60083 MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); 60084 MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); 60085 MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); 60086 MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); 60087 MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); 60088 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); 60089 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); 60090 MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); 60091 MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); 60092 MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); 60093 MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); 60094 MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); 60095 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); 60096 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); 60097 #ifndef MA_DR_WAV_NO_CONVERSION_API 60098 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); 60099 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); 60100 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); 60101 MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); 60102 MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); 60103 MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); 60104 MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); 60105 MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); 60106 MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); 60107 MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); 60108 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); 60109 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); 60110 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); 60111 MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); 60112 MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); 60113 MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); 60114 MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); 60115 MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); 60116 MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); 60117 MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); 60118 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); 60119 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); 60120 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); 60121 MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); 60122 MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); 60123 MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); 60124 MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); 60125 MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); 60126 MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); 60127 MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); 60128 #endif 60129 #ifndef MA_DR_WAV_NO_STDIO 60130 MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); 60131 MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60132 MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); 60133 MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60134 MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60135 MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60136 MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); 60137 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); 60138 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60139 MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); 60140 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); 60141 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60142 #endif 60143 MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); 60144 MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60145 MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); 60146 MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); 60147 MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); 60148 MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60149 #ifndef MA_DR_WAV_NO_CONVERSION_API 60150 MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60151 MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60152 MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60153 #ifndef MA_DR_WAV_NO_STDIO 60154 MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60155 MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60156 MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60157 MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60158 MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60159 MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60160 #endif 60161 MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60162 MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60163 MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); 60164 #endif 60165 MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); 60166 MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); 60167 MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); 60168 MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); 60169 MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); 60170 MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); 60171 MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); 60172 MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); 60173 MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); 60174 MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); 60175 #ifdef __cplusplus 60176 } 60177 #endif 60178 #endif 60179 /* dr_wav_h end */ 60180 #endif /* MA_NO_WAV */ 60181 60182 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) 60183 /* dr_flac_h begin */ 60184 #ifndef ma_dr_flac_h 60185 #define ma_dr_flac_h 60186 #ifdef __cplusplus 60187 extern "C" { 60188 #endif 60189 #define MA_DR_FLAC_STRINGIFY(x) #x 60190 #define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) 60191 #define MA_DR_FLAC_VERSION_MAJOR 0 60192 #define MA_DR_FLAC_VERSION_MINOR 12 60193 #define MA_DR_FLAC_VERSION_REVISION 42 60194 #define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) 60195 #include <stddef.h> 60196 #if defined(_MSC_VER) && _MSC_VER >= 1700 60197 #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) 60198 #elif (defined(__GNUC__) && __GNUC__ >= 4) 60199 #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) 60200 #elif defined(__has_feature) 60201 #if __has_feature(attribute_deprecated) 60202 #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) 60203 #else 60204 #define MA_DR_FLAC_DEPRECATED 60205 #endif 60206 #else 60207 #define MA_DR_FLAC_DEPRECATED 60208 #endif 60209 MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); 60210 MA_API const char* ma_dr_flac_version_string(void); 60211 #ifndef MA_DR_FLAC_BUFFER_SIZE 60212 #define MA_DR_FLAC_BUFFER_SIZE 4096 60213 #endif 60214 #ifdef MA_64BIT 60215 typedef ma_uint64 ma_dr_flac_cache_t; 60216 #else 60217 typedef ma_uint32 ma_dr_flac_cache_t; 60218 #endif 60219 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 60220 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 60221 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 60222 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 60223 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 60224 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 60225 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 60226 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 60227 #define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 60228 #define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 60229 #define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 60230 #define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 60231 #define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 60232 #define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 60233 #define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 60234 #define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 60235 #define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 60236 #define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 60237 #define MA_DR_FLAC_PICTURE_TYPE_BAND 10 60238 #define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 60239 #define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 60240 #define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 60241 #define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 60242 #define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 60243 #define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 60244 #define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 60245 #define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 60246 #define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 60247 #define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 60248 typedef enum 60249 { 60250 ma_dr_flac_container_native, 60251 ma_dr_flac_container_ogg, 60252 ma_dr_flac_container_unknown 60253 } ma_dr_flac_container; 60254 typedef enum 60255 { 60256 ma_dr_flac_seek_origin_start, 60257 ma_dr_flac_seek_origin_current 60258 } ma_dr_flac_seek_origin; 60259 typedef struct 60260 { 60261 ma_uint64 firstPCMFrame; 60262 ma_uint64 flacFrameOffset; 60263 ma_uint16 pcmFrameCount; 60264 } ma_dr_flac_seekpoint; 60265 typedef struct 60266 { 60267 ma_uint16 minBlockSizeInPCMFrames; 60268 ma_uint16 maxBlockSizeInPCMFrames; 60269 ma_uint32 minFrameSizeInPCMFrames; 60270 ma_uint32 maxFrameSizeInPCMFrames; 60271 ma_uint32 sampleRate; 60272 ma_uint8 channels; 60273 ma_uint8 bitsPerSample; 60274 ma_uint64 totalPCMFrameCount; 60275 ma_uint8 md5[16]; 60276 } ma_dr_flac_streaminfo; 60277 typedef struct 60278 { 60279 ma_uint32 type; 60280 const void* pRawData; 60281 ma_uint32 rawDataSize; 60282 union 60283 { 60284 ma_dr_flac_streaminfo streaminfo; 60285 struct 60286 { 60287 int unused; 60288 } padding; 60289 struct 60290 { 60291 ma_uint32 id; 60292 const void* pData; 60293 ma_uint32 dataSize; 60294 } application; 60295 struct 60296 { 60297 ma_uint32 seekpointCount; 60298 const ma_dr_flac_seekpoint* pSeekpoints; 60299 } seektable; 60300 struct 60301 { 60302 ma_uint32 vendorLength; 60303 const char* vendor; 60304 ma_uint32 commentCount; 60305 const void* pComments; 60306 } vorbis_comment; 60307 struct 60308 { 60309 char catalog[128]; 60310 ma_uint64 leadInSampleCount; 60311 ma_bool32 isCD; 60312 ma_uint8 trackCount; 60313 const void* pTrackData; 60314 } cuesheet; 60315 struct 60316 { 60317 ma_uint32 type; 60318 ma_uint32 mimeLength; 60319 const char* mime; 60320 ma_uint32 descriptionLength; 60321 const char* description; 60322 ma_uint32 width; 60323 ma_uint32 height; 60324 ma_uint32 colorDepth; 60325 ma_uint32 indexColorCount; 60326 ma_uint32 pictureDataSize; 60327 const ma_uint8* pPictureData; 60328 } picture; 60329 } data; 60330 } ma_dr_flac_metadata; 60331 typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); 60332 typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); 60333 typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); 60334 typedef struct 60335 { 60336 const ma_uint8* data; 60337 size_t dataSize; 60338 size_t currentReadPos; 60339 } ma_dr_flac__memory_stream; 60340 typedef struct 60341 { 60342 ma_dr_flac_read_proc onRead; 60343 ma_dr_flac_seek_proc onSeek; 60344 void* pUserData; 60345 size_t unalignedByteCount; 60346 ma_dr_flac_cache_t unalignedCache; 60347 ma_uint32 nextL2Line; 60348 ma_uint32 consumedBits; 60349 ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; 60350 ma_dr_flac_cache_t cache; 60351 ma_uint16 crc16; 60352 ma_dr_flac_cache_t crc16Cache; 60353 ma_uint32 crc16CacheIgnoredBytes; 60354 } ma_dr_flac_bs; 60355 typedef struct 60356 { 60357 ma_uint8 subframeType; 60358 ma_uint8 wastedBitsPerSample; 60359 ma_uint8 lpcOrder; 60360 ma_int32* pSamplesS32; 60361 } ma_dr_flac_subframe; 60362 typedef struct 60363 { 60364 ma_uint64 pcmFrameNumber; 60365 ma_uint32 flacFrameNumber; 60366 ma_uint32 sampleRate; 60367 ma_uint16 blockSizeInPCMFrames; 60368 ma_uint8 channelAssignment; 60369 ma_uint8 bitsPerSample; 60370 ma_uint8 crc8; 60371 } ma_dr_flac_frame_header; 60372 typedef struct 60373 { 60374 ma_dr_flac_frame_header header; 60375 ma_uint32 pcmFramesRemaining; 60376 ma_dr_flac_subframe subframes[8]; 60377 } ma_dr_flac_frame; 60378 typedef struct 60379 { 60380 ma_dr_flac_meta_proc onMeta; 60381 void* pUserDataMD; 60382 ma_allocation_callbacks allocationCallbacks; 60383 ma_uint32 sampleRate; 60384 ma_uint8 channels; 60385 ma_uint8 bitsPerSample; 60386 ma_uint16 maxBlockSizeInPCMFrames; 60387 ma_uint64 totalPCMFrameCount; 60388 ma_dr_flac_container container; 60389 ma_uint32 seekpointCount; 60390 ma_dr_flac_frame currentFLACFrame; 60391 ma_uint64 currentPCMFrame; 60392 ma_uint64 firstFLACFramePosInBytes; 60393 ma_dr_flac__memory_stream memoryStream; 60394 ma_int32* pDecodedSamples; 60395 ma_dr_flac_seekpoint* pSeekpoints; 60396 void* _oggbs; 60397 ma_bool32 _noSeekTableSeek : 1; 60398 ma_bool32 _noBinarySearchSeek : 1; 60399 ma_bool32 _noBruteForceSeek : 1; 60400 ma_dr_flac_bs bs; 60401 ma_uint8 pExtraData[1]; 60402 } ma_dr_flac; 60403 MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60404 MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60405 MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60406 MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60407 MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); 60408 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); 60409 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); 60410 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); 60411 MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); 60412 #ifndef MA_DR_FLAC_NO_STDIO 60413 MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); 60414 MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); 60415 MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60416 MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60417 #endif 60418 MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); 60419 MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60420 MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60421 MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60422 MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60423 #ifndef MA_DR_FLAC_NO_STDIO 60424 MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60425 MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60426 MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60427 #endif 60428 MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60429 MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60430 MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60431 MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); 60432 typedef struct 60433 { 60434 ma_uint32 countRemaining; 60435 const char* pRunningData; 60436 } ma_dr_flac_vorbis_comment_iterator; 60437 MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); 60438 MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); 60439 typedef struct 60440 { 60441 ma_uint32 countRemaining; 60442 const char* pRunningData; 60443 } ma_dr_flac_cuesheet_track_iterator; 60444 typedef struct 60445 { 60446 ma_uint64 offset; 60447 ma_uint8 index; 60448 ma_uint8 reserved[3]; 60449 } ma_dr_flac_cuesheet_track_index; 60450 typedef struct 60451 { 60452 ma_uint64 offset; 60453 ma_uint8 trackNumber; 60454 char ISRC[12]; 60455 ma_bool8 isAudio; 60456 ma_bool8 preEmphasis; 60457 ma_uint8 indexCount; 60458 const ma_dr_flac_cuesheet_track_index* pIndexPoints; 60459 } ma_dr_flac_cuesheet_track; 60460 MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); 60461 MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); 60462 #ifdef __cplusplus 60463 } 60464 #endif 60465 #endif 60466 /* dr_flac_h end */ 60467 #endif /* MA_NO_FLAC */ 60468 60469 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) 60470 /* dr_mp3_h begin */ 60471 #ifndef ma_dr_mp3_h 60472 #define ma_dr_mp3_h 60473 #ifdef __cplusplus 60474 extern "C" { 60475 #endif 60476 #define MA_DR_MP3_STRINGIFY(x) #x 60477 #define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) 60478 #define MA_DR_MP3_VERSION_MAJOR 0 60479 #define MA_DR_MP3_VERSION_MINOR 6 60480 #define MA_DR_MP3_VERSION_REVISION 38 60481 #define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) 60482 #include <stddef.h> 60483 #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 60484 #define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) 60485 MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); 60486 MA_API const char* ma_dr_mp3_version_string(void); 60487 typedef struct 60488 { 60489 int frame_bytes, channels, hz, layer, bitrate_kbps; 60490 } ma_dr_mp3dec_frame_info; 60491 typedef struct 60492 { 60493 float mdct_overlap[2][9*32], qmf_state[15*2*32]; 60494 int reserv, free_format_bytes; 60495 ma_uint8 header[4], reserv_buf[511]; 60496 } ma_dr_mp3dec; 60497 MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); 60498 MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); 60499 MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); 60500 typedef enum 60501 { 60502 ma_dr_mp3_seek_origin_start, 60503 ma_dr_mp3_seek_origin_current 60504 } ma_dr_mp3_seek_origin; 60505 typedef struct 60506 { 60507 ma_uint64 seekPosInBytes; 60508 ma_uint64 pcmFrameIndex; 60509 ma_uint16 mp3FramesToDiscard; 60510 ma_uint16 pcmFramesToDiscard; 60511 } ma_dr_mp3_seek_point; 60512 typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); 60513 typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); 60514 typedef struct 60515 { 60516 ma_uint32 channels; 60517 ma_uint32 sampleRate; 60518 } ma_dr_mp3_config; 60519 typedef struct 60520 { 60521 ma_dr_mp3dec decoder; 60522 ma_uint32 channels; 60523 ma_uint32 sampleRate; 60524 ma_dr_mp3_read_proc onRead; 60525 ma_dr_mp3_seek_proc onSeek; 60526 void* pUserData; 60527 ma_allocation_callbacks allocationCallbacks; 60528 ma_uint32 mp3FrameChannels; 60529 ma_uint32 mp3FrameSampleRate; 60530 ma_uint32 pcmFramesConsumedInMP3Frame; 60531 ma_uint32 pcmFramesRemainingInMP3Frame; 60532 ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; 60533 ma_uint64 currentPCMFrame; 60534 ma_uint64 streamCursor; 60535 ma_dr_mp3_seek_point* pSeekPoints; 60536 ma_uint32 seekPointCount; 60537 size_t dataSize; 60538 size_t dataCapacity; 60539 size_t dataConsumed; 60540 ma_uint8* pData; 60541 ma_bool32 atEnd : 1; 60542 struct 60543 { 60544 const ma_uint8* pData; 60545 size_t dataSize; 60546 size_t currentReadPos; 60547 } memory; 60548 } ma_dr_mp3; 60549 MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); 60550 MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); 60551 #ifndef MA_DR_MP3_NO_STDIO 60552 MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); 60553 MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); 60554 #endif 60555 MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); 60556 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); 60557 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); 60558 MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); 60559 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); 60560 MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); 60561 MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); 60562 MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); 60563 MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); 60564 MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60565 MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60566 MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60567 MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60568 #ifndef MA_DR_MP3_NO_STDIO 60569 MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60570 MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); 60571 #endif 60572 MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); 60573 MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); 60574 #ifdef __cplusplus 60575 } 60576 #endif 60577 #endif 60578 /* dr_mp3_h end */ 60579 #endif /* MA_NO_MP3 */ 60580 60581 60582 /************************************************************************************************************************************************************** 60583 60584 Decoding 60585 60586 **************************************************************************************************************************************************************/ 60587 #ifndef MA_NO_DECODING 60588 60589 static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) 60590 { 60591 MA_ASSERT(pDecoder != NULL); 60592 60593 return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); 60594 } 60595 60596 static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) 60597 { 60598 MA_ASSERT(pDecoder != NULL); 60599 60600 return pDecoder->onSeek(pDecoder, byteOffset, origin); 60601 } 60602 60603 static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) 60604 { 60605 MA_ASSERT(pDecoder != NULL); 60606 60607 if (pDecoder->onTell == NULL) { 60608 return MA_NOT_IMPLEMENTED; 60609 } 60610 60611 return pDecoder->onTell(pDecoder, pCursor); 60612 } 60613 60614 60615 MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) 60616 { 60617 ma_decoding_backend_config config; 60618 60619 MA_ZERO_OBJECT(&config); 60620 config.preferredFormat = preferredFormat; 60621 config.seekPointCount = seekPointCount; 60622 60623 return config; 60624 } 60625 60626 60627 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) 60628 { 60629 ma_decoder_config config; 60630 MA_ZERO_OBJECT(&config); 60631 config.format = outputFormat; 60632 config.channels = outputChannels; 60633 config.sampleRate = outputSampleRate; 60634 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ 60635 config.encodingFormat = ma_encoding_format_unknown; 60636 60637 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ 60638 60639 return config; 60640 } 60641 60642 MA_API ma_decoder_config ma_decoder_config_init_default() 60643 { 60644 return ma_decoder_config_init(ma_format_unknown, 0, 0); 60645 } 60646 60647 MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) 60648 { 60649 ma_decoder_config config; 60650 if (pConfig != NULL) { 60651 config = *pConfig; 60652 } else { 60653 MA_ZERO_OBJECT(&config); 60654 } 60655 60656 return config; 60657 } 60658 60659 static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) 60660 { 60661 ma_result result; 60662 ma_data_converter_config converterConfig; 60663 ma_format internalFormat; 60664 ma_uint32 internalChannels; 60665 ma_uint32 internalSampleRate; 60666 ma_channel internalChannelMap[MA_MAX_CHANNELS]; 60667 60668 MA_ASSERT(pDecoder != NULL); 60669 MA_ASSERT(pConfig != NULL); 60670 60671 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); 60672 if (result != MA_SUCCESS) { 60673 return result; /* Failed to retrieve the internal data format. */ 60674 } 60675 60676 60677 /* Make sure we're not asking for too many channels. */ 60678 if (pConfig->channels > MA_MAX_CHANNELS) { 60679 return MA_INVALID_ARGS; 60680 } 60681 60682 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ 60683 if (internalChannels > MA_MAX_CHANNELS) { 60684 return MA_INVALID_ARGS; 60685 } 60686 60687 60688 /* Output format. */ 60689 if (pConfig->format == ma_format_unknown) { 60690 pDecoder->outputFormat = internalFormat; 60691 } else { 60692 pDecoder->outputFormat = pConfig->format; 60693 } 60694 60695 if (pConfig->channels == 0) { 60696 pDecoder->outputChannels = internalChannels; 60697 } else { 60698 pDecoder->outputChannels = pConfig->channels; 60699 } 60700 60701 if (pConfig->sampleRate == 0) { 60702 pDecoder->outputSampleRate = internalSampleRate; 60703 } else { 60704 pDecoder->outputSampleRate = pConfig->sampleRate; 60705 } 60706 60707 converterConfig = ma_data_converter_config_init( 60708 internalFormat, pDecoder->outputFormat, 60709 internalChannels, pDecoder->outputChannels, 60710 internalSampleRate, pDecoder->outputSampleRate 60711 ); 60712 converterConfig.pChannelMapIn = internalChannelMap; 60713 converterConfig.pChannelMapOut = pConfig->pChannelMap; 60714 converterConfig.channelMixMode = pConfig->channelMixMode; 60715 converterConfig.ditherMode = pConfig->ditherMode; 60716 converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ 60717 converterConfig.resampling = pConfig->resampling; 60718 60719 result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); 60720 if (result != MA_SUCCESS) { 60721 return result; 60722 } 60723 60724 /* 60725 Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll 60726 need this if the data converter does not support calculation of the required input frame count. To 60727 determine support for this we'll just run a test. 60728 */ 60729 { 60730 ma_uint64 unused; 60731 60732 result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); 60733 if (result != MA_SUCCESS) { 60734 /* 60735 We were unable to calculate the required input frame count which means we'll need to use 60736 a heap-allocated cache. 60737 */ 60738 ma_uint64 inputCacheCapSizeInBytes; 60739 60740 pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); 60741 60742 /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ 60743 inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); 60744 if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { 60745 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); 60746 return MA_OUT_OF_MEMORY; 60747 } 60748 60749 pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ 60750 if (pDecoder->pInputCache == NULL) { 60751 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); 60752 return MA_OUT_OF_MEMORY; 60753 } 60754 } 60755 } 60756 60757 return MA_SUCCESS; 60758 } 60759 60760 60761 60762 static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) 60763 { 60764 ma_decoder* pDecoder = (ma_decoder*)pUserData; 60765 MA_ASSERT(pDecoder != NULL); 60766 60767 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); 60768 } 60769 60770 static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) 60771 { 60772 ma_decoder* pDecoder = (ma_decoder*)pUserData; 60773 MA_ASSERT(pDecoder != NULL); 60774 60775 return ma_decoder_seek_bytes(pDecoder, offset, origin); 60776 } 60777 60778 static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) 60779 { 60780 ma_decoder* pDecoder = (ma_decoder*)pUserData; 60781 MA_ASSERT(pDecoder != NULL); 60782 60783 return ma_decoder_tell_bytes(pDecoder, pCursor); 60784 } 60785 60786 60787 static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60788 { 60789 ma_result result; 60790 ma_decoding_backend_config backendConfig; 60791 ma_data_source* pBackend; 60792 60793 MA_ASSERT(pVTable != NULL); 60794 MA_ASSERT(pConfig != NULL); 60795 MA_ASSERT(pDecoder != NULL); 60796 60797 if (pVTable->onInit == NULL) { 60798 return MA_NOT_IMPLEMENTED; 60799 } 60800 60801 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); 60802 60803 result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); 60804 if (result != MA_SUCCESS) { 60805 return result; /* Failed to initialize the backend from this vtable. */ 60806 } 60807 60808 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ 60809 pDecoder->pBackend = pBackend; 60810 pDecoder->pBackendVTable = pVTable; 60811 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; 60812 60813 return MA_SUCCESS; 60814 } 60815 60816 static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60817 { 60818 ma_result result; 60819 ma_decoding_backend_config backendConfig; 60820 ma_data_source* pBackend; 60821 60822 MA_ASSERT(pVTable != NULL); 60823 MA_ASSERT(pConfig != NULL); 60824 MA_ASSERT(pDecoder != NULL); 60825 60826 if (pVTable->onInitFile == NULL) { 60827 return MA_NOT_IMPLEMENTED; 60828 } 60829 60830 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); 60831 60832 result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); 60833 if (result != MA_SUCCESS) { 60834 return result; /* Failed to initialize the backend from this vtable. */ 60835 } 60836 60837 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ 60838 pDecoder->pBackend = pBackend; 60839 pDecoder->pBackendVTable = pVTable; 60840 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; 60841 60842 return MA_SUCCESS; 60843 } 60844 60845 static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60846 { 60847 ma_result result; 60848 ma_decoding_backend_config backendConfig; 60849 ma_data_source* pBackend; 60850 60851 MA_ASSERT(pVTable != NULL); 60852 MA_ASSERT(pConfig != NULL); 60853 MA_ASSERT(pDecoder != NULL); 60854 60855 if (pVTable->onInitFileW == NULL) { 60856 return MA_NOT_IMPLEMENTED; 60857 } 60858 60859 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); 60860 60861 result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); 60862 if (result != MA_SUCCESS) { 60863 return result; /* Failed to initialize the backend from this vtable. */ 60864 } 60865 60866 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ 60867 pDecoder->pBackend = pBackend; 60868 pDecoder->pBackendVTable = pVTable; 60869 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; 60870 60871 return MA_SUCCESS; 60872 } 60873 60874 static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60875 { 60876 ma_result result; 60877 ma_decoding_backend_config backendConfig; 60878 ma_data_source* pBackend; 60879 60880 MA_ASSERT(pVTable != NULL); 60881 MA_ASSERT(pConfig != NULL); 60882 MA_ASSERT(pDecoder != NULL); 60883 60884 if (pVTable->onInitMemory == NULL) { 60885 return MA_NOT_IMPLEMENTED; 60886 } 60887 60888 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); 60889 60890 result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); 60891 if (result != MA_SUCCESS) { 60892 return result; /* Failed to initialize the backend from this vtable. */ 60893 } 60894 60895 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ 60896 pDecoder->pBackend = pBackend; 60897 pDecoder->pBackendVTable = pVTable; 60898 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; 60899 60900 return MA_SUCCESS; 60901 } 60902 60903 60904 60905 static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60906 { 60907 ma_result result = MA_NO_BACKEND; 60908 size_t ivtable; 60909 60910 MA_ASSERT(pConfig != NULL); 60911 MA_ASSERT(pDecoder != NULL); 60912 60913 if (pConfig->ppCustomBackendVTables == NULL) { 60914 return MA_NO_BACKEND; 60915 } 60916 60917 /* The order each backend is listed is what defines the priority. */ 60918 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { 60919 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; 60920 if (pVTable != NULL) { 60921 result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); 60922 if (result == MA_SUCCESS) { 60923 return MA_SUCCESS; 60924 } else { 60925 /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ 60926 result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); 60927 if (result != MA_SUCCESS) { 60928 return result; /* Failed to seek back to the start. */ 60929 } 60930 } 60931 } else { 60932 /* No vtable. */ 60933 } 60934 } 60935 60936 /* Getting here means we couldn't find a backend. */ 60937 return MA_NO_BACKEND; 60938 } 60939 60940 static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60941 { 60942 ma_result result = MA_NO_BACKEND; 60943 size_t ivtable; 60944 60945 MA_ASSERT(pConfig != NULL); 60946 MA_ASSERT(pDecoder != NULL); 60947 60948 if (pConfig->ppCustomBackendVTables == NULL) { 60949 return MA_NO_BACKEND; 60950 } 60951 60952 /* The order each backend is listed is what defines the priority. */ 60953 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { 60954 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; 60955 if (pVTable != NULL) { 60956 result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); 60957 if (result == MA_SUCCESS) { 60958 return MA_SUCCESS; 60959 } 60960 } else { 60961 /* No vtable. */ 60962 } 60963 } 60964 60965 /* Getting here means we couldn't find a backend. */ 60966 return MA_NO_BACKEND; 60967 } 60968 60969 static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60970 { 60971 ma_result result = MA_NO_BACKEND; 60972 size_t ivtable; 60973 60974 MA_ASSERT(pConfig != NULL); 60975 MA_ASSERT(pDecoder != NULL); 60976 60977 if (pConfig->ppCustomBackendVTables == NULL) { 60978 return MA_NO_BACKEND; 60979 } 60980 60981 /* The order each backend is listed is what defines the priority. */ 60982 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { 60983 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; 60984 if (pVTable != NULL) { 60985 result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); 60986 if (result == MA_SUCCESS) { 60987 return MA_SUCCESS; 60988 } 60989 } else { 60990 /* No vtable. */ 60991 } 60992 } 60993 60994 /* Getting here means we couldn't find a backend. */ 60995 return MA_NO_BACKEND; 60996 } 60997 60998 static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 60999 { 61000 ma_result result = MA_NO_BACKEND; 61001 size_t ivtable; 61002 61003 MA_ASSERT(pConfig != NULL); 61004 MA_ASSERT(pDecoder != NULL); 61005 61006 if (pConfig->ppCustomBackendVTables == NULL) { 61007 return MA_NO_BACKEND; 61008 } 61009 61010 /* The order each backend is listed is what defines the priority. */ 61011 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { 61012 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; 61013 if (pVTable != NULL) { 61014 result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); 61015 if (result == MA_SUCCESS) { 61016 return MA_SUCCESS; 61017 } 61018 } else { 61019 /* No vtable. */ 61020 } 61021 } 61022 61023 /* Getting here means we couldn't find a backend. */ 61024 return MA_NO_BACKEND; 61025 } 61026 61027 61028 /* WAV */ 61029 #ifdef ma_dr_wav_h 61030 #define MA_HAS_WAV 61031 61032 typedef struct 61033 { 61034 ma_data_source_base ds; 61035 ma_read_proc onRead; 61036 ma_seek_proc onSeek; 61037 ma_tell_proc onTell; 61038 void* pReadSeekTellUserData; 61039 ma_format format; /* Can be f32, s16 or s32. */ 61040 #if !defined(MA_NO_WAV) 61041 ma_dr_wav dr; 61042 #endif 61043 } ma_wav; 61044 61045 MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); 61046 MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); 61047 MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); 61048 MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); 61049 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); 61050 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 61051 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); 61052 MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 61053 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); 61054 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); 61055 61056 61057 static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 61058 { 61059 return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); 61060 } 61061 61062 static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 61063 { 61064 return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); 61065 } 61066 61067 static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 61068 { 61069 return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 61070 } 61071 61072 static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 61073 { 61074 return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); 61075 } 61076 61077 static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 61078 { 61079 return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); 61080 } 61081 61082 static ma_data_source_vtable g_ma_wav_ds_vtable = 61083 { 61084 ma_wav_ds_read, 61085 ma_wav_ds_seek, 61086 ma_wav_ds_get_data_format, 61087 ma_wav_ds_get_cursor, 61088 ma_wav_ds_get_length, 61089 NULL, /* onSetLooping */ 61090 0 61091 }; 61092 61093 61094 #if !defined(MA_NO_WAV) 61095 static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) 61096 { 61097 ma_wav* pWav = (ma_wav*)pUserData; 61098 ma_result result; 61099 size_t bytesRead; 61100 61101 MA_ASSERT(pWav != NULL); 61102 61103 result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); 61104 (void)result; 61105 61106 return bytesRead; 61107 } 61108 61109 static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) 61110 { 61111 ma_wav* pWav = (ma_wav*)pUserData; 61112 ma_result result; 61113 ma_seek_origin maSeekOrigin; 61114 61115 MA_ASSERT(pWav != NULL); 61116 61117 maSeekOrigin = ma_seek_origin_start; 61118 if (origin == ma_dr_wav_seek_origin_current) { 61119 maSeekOrigin = ma_seek_origin_current; 61120 } 61121 61122 result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); 61123 if (result != MA_SUCCESS) { 61124 return MA_FALSE; 61125 } 61126 61127 return MA_TRUE; 61128 } 61129 #endif 61130 61131 static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) 61132 { 61133 ma_result result; 61134 ma_data_source_config dataSourceConfig; 61135 61136 if (pWav == NULL) { 61137 return MA_INVALID_ARGS; 61138 } 61139 61140 MA_ZERO_OBJECT(pWav); 61141 pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ 61142 61143 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { 61144 pWav->format = pConfig->preferredFormat; 61145 } else { 61146 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ 61147 } 61148 61149 dataSourceConfig = ma_data_source_config_init(); 61150 dataSourceConfig.vtable = &g_ma_wav_ds_vtable; 61151 61152 result = ma_data_source_init(&dataSourceConfig, &pWav->ds); 61153 if (result != MA_SUCCESS) { 61154 return result; /* Failed to initialize the base data source. */ 61155 } 61156 61157 return MA_SUCCESS; 61158 } 61159 61160 static ma_result ma_wav_post_init(ma_wav* pWav) 61161 { 61162 /* 61163 If an explicit format was not specified, try picking the closest match based on the internal 61164 format. The format needs to be supported by miniaudio. 61165 */ 61166 if (pWav->format == ma_format_unknown) { 61167 switch (pWav->dr.translatedFormatTag) 61168 { 61169 case MA_DR_WAVE_FORMAT_PCM: 61170 { 61171 if (pWav->dr.bitsPerSample == 8) { 61172 pWav->format = ma_format_u8; 61173 } else if (pWav->dr.bitsPerSample == 16) { 61174 pWav->format = ma_format_s16; 61175 } else if (pWav->dr.bitsPerSample == 24) { 61176 pWav->format = ma_format_s24; 61177 } else if (pWav->dr.bitsPerSample == 32) { 61178 pWav->format = ma_format_s32; 61179 } 61180 } break; 61181 61182 case MA_DR_WAVE_FORMAT_IEEE_FLOAT: 61183 { 61184 if (pWav->dr.bitsPerSample == 32) { 61185 pWav->format = ma_format_f32; 61186 } 61187 } break; 61188 61189 default: break; 61190 } 61191 61192 /* Fall back to f32 if we couldn't find anything. */ 61193 if (pWav->format == ma_format_unknown) { 61194 pWav->format = ma_format_f32; 61195 } 61196 } 61197 61198 return MA_SUCCESS; 61199 } 61200 61201 MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) 61202 { 61203 ma_result result; 61204 61205 result = ma_wav_init_internal(pConfig, pWav); 61206 if (result != MA_SUCCESS) { 61207 return result; 61208 } 61209 61210 if (onRead == NULL || onSeek == NULL) { 61211 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ 61212 } 61213 61214 pWav->onRead = onRead; 61215 pWav->onSeek = onSeek; 61216 pWav->onTell = onTell; 61217 pWav->pReadSeekTellUserData = pReadSeekTellUserData; 61218 61219 #if !defined(MA_NO_WAV) 61220 { 61221 ma_bool32 wavResult; 61222 61223 wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); 61224 if (wavResult != MA_TRUE) { 61225 return MA_INVALID_FILE; 61226 } 61227 61228 ma_wav_post_init(pWav); 61229 61230 return MA_SUCCESS; 61231 } 61232 #else 61233 { 61234 /* wav is disabled. */ 61235 (void)pAllocationCallbacks; 61236 return MA_NOT_IMPLEMENTED; 61237 } 61238 #endif 61239 } 61240 61241 MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) 61242 { 61243 ma_result result; 61244 61245 result = ma_wav_init_internal(pConfig, pWav); 61246 if (result != MA_SUCCESS) { 61247 return result; 61248 } 61249 61250 #if !defined(MA_NO_WAV) 61251 { 61252 ma_bool32 wavResult; 61253 61254 wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); 61255 if (wavResult != MA_TRUE) { 61256 return MA_INVALID_FILE; 61257 } 61258 61259 ma_wav_post_init(pWav); 61260 61261 return MA_SUCCESS; 61262 } 61263 #else 61264 { 61265 /* wav is disabled. */ 61266 (void)pFilePath; 61267 (void)pAllocationCallbacks; 61268 return MA_NOT_IMPLEMENTED; 61269 } 61270 #endif 61271 } 61272 61273 MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) 61274 { 61275 ma_result result; 61276 61277 result = ma_wav_init_internal(pConfig, pWav); 61278 if (result != MA_SUCCESS) { 61279 return result; 61280 } 61281 61282 #if !defined(MA_NO_WAV) 61283 { 61284 ma_bool32 wavResult; 61285 61286 wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); 61287 if (wavResult != MA_TRUE) { 61288 return MA_INVALID_FILE; 61289 } 61290 61291 ma_wav_post_init(pWav); 61292 61293 return MA_SUCCESS; 61294 } 61295 #else 61296 { 61297 /* wav is disabled. */ 61298 (void)pFilePath; 61299 (void)pAllocationCallbacks; 61300 return MA_NOT_IMPLEMENTED; 61301 } 61302 #endif 61303 } 61304 61305 MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) 61306 { 61307 ma_result result; 61308 61309 result = ma_wav_init_internal(pConfig, pWav); 61310 if (result != MA_SUCCESS) { 61311 return result; 61312 } 61313 61314 #if !defined(MA_NO_WAV) 61315 { 61316 ma_bool32 wavResult; 61317 61318 wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); 61319 if (wavResult != MA_TRUE) { 61320 return MA_INVALID_FILE; 61321 } 61322 61323 ma_wav_post_init(pWav); 61324 61325 return MA_SUCCESS; 61326 } 61327 #else 61328 { 61329 /* wav is disabled. */ 61330 (void)pData; 61331 (void)dataSize; 61332 (void)pAllocationCallbacks; 61333 return MA_NOT_IMPLEMENTED; 61334 } 61335 #endif 61336 } 61337 61338 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) 61339 { 61340 if (pWav == NULL) { 61341 return; 61342 } 61343 61344 (void)pAllocationCallbacks; 61345 61346 #if !defined(MA_NO_WAV) 61347 { 61348 ma_dr_wav_uninit(&pWav->dr); 61349 } 61350 #else 61351 { 61352 /* wav is disabled. Should never hit this since initialization would have failed. */ 61353 MA_ASSERT(MA_FALSE); 61354 } 61355 #endif 61356 61357 ma_data_source_uninit(&pWav->ds); 61358 } 61359 61360 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 61361 { 61362 if (pFramesRead != NULL) { 61363 *pFramesRead = 0; 61364 } 61365 61366 if (frameCount == 0) { 61367 return MA_INVALID_ARGS; 61368 } 61369 61370 if (pWav == NULL) { 61371 return MA_INVALID_ARGS; 61372 } 61373 61374 #if !defined(MA_NO_WAV) 61375 { 61376 /* We always use floating point format. */ 61377 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ 61378 ma_uint64 totalFramesRead = 0; 61379 ma_format format; 61380 61381 ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); 61382 61383 switch (format) 61384 { 61385 case ma_format_f32: 61386 { 61387 totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); 61388 } break; 61389 61390 case ma_format_s16: 61391 { 61392 totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); 61393 } break; 61394 61395 case ma_format_s32: 61396 { 61397 totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); 61398 } break; 61399 61400 /* Fallback to a raw read. */ 61401 case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ 61402 default: 61403 { 61404 totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); 61405 } break; 61406 } 61407 61408 /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ 61409 if (totalFramesRead == 0) { 61410 result = MA_AT_END; 61411 } 61412 61413 if (pFramesRead != NULL) { 61414 *pFramesRead = totalFramesRead; 61415 } 61416 61417 if (result == MA_SUCCESS && totalFramesRead == 0) { 61418 result = MA_AT_END; 61419 } 61420 61421 return result; 61422 } 61423 #else 61424 { 61425 /* wav is disabled. Should never hit this since initialization would have failed. */ 61426 MA_ASSERT(MA_FALSE); 61427 61428 (void)pFramesOut; 61429 (void)frameCount; 61430 (void)pFramesRead; 61431 61432 return MA_NOT_IMPLEMENTED; 61433 } 61434 #endif 61435 } 61436 61437 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) 61438 { 61439 if (pWav == NULL) { 61440 return MA_INVALID_ARGS; 61441 } 61442 61443 #if !defined(MA_NO_WAV) 61444 { 61445 ma_bool32 wavResult; 61446 61447 wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); 61448 if (wavResult != MA_TRUE) { 61449 return MA_ERROR; 61450 } 61451 61452 return MA_SUCCESS; 61453 } 61454 #else 61455 { 61456 /* wav is disabled. Should never hit this since initialization would have failed. */ 61457 MA_ASSERT(MA_FALSE); 61458 61459 (void)frameIndex; 61460 61461 return MA_NOT_IMPLEMENTED; 61462 } 61463 #endif 61464 } 61465 61466 MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 61467 { 61468 /* Defaults for safety. */ 61469 if (pFormat != NULL) { 61470 *pFormat = ma_format_unknown; 61471 } 61472 if (pChannels != NULL) { 61473 *pChannels = 0; 61474 } 61475 if (pSampleRate != NULL) { 61476 *pSampleRate = 0; 61477 } 61478 if (pChannelMap != NULL) { 61479 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 61480 } 61481 61482 if (pWav == NULL) { 61483 return MA_INVALID_OPERATION; 61484 } 61485 61486 if (pFormat != NULL) { 61487 *pFormat = pWav->format; 61488 } 61489 61490 #if !defined(MA_NO_WAV) 61491 { 61492 if (pChannels != NULL) { 61493 *pChannels = pWav->dr.channels; 61494 } 61495 61496 if (pSampleRate != NULL) { 61497 *pSampleRate = pWav->dr.sampleRate; 61498 } 61499 61500 if (pChannelMap != NULL) { 61501 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); 61502 } 61503 61504 return MA_SUCCESS; 61505 } 61506 #else 61507 { 61508 /* wav is disabled. Should never hit this since initialization would have failed. */ 61509 MA_ASSERT(MA_FALSE); 61510 return MA_NOT_IMPLEMENTED; 61511 } 61512 #endif 61513 } 61514 61515 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) 61516 { 61517 if (pCursor == NULL) { 61518 return MA_INVALID_ARGS; 61519 } 61520 61521 *pCursor = 0; /* Safety. */ 61522 61523 if (pWav == NULL) { 61524 return MA_INVALID_ARGS; 61525 } 61526 61527 #if !defined(MA_NO_WAV) 61528 { 61529 ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); 61530 if (wavResult != MA_SUCCESS) { 61531 return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ 61532 } 61533 61534 return MA_SUCCESS; 61535 } 61536 #else 61537 { 61538 /* wav is disabled. Should never hit this since initialization would have failed. */ 61539 MA_ASSERT(MA_FALSE); 61540 return MA_NOT_IMPLEMENTED; 61541 } 61542 #endif 61543 } 61544 61545 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) 61546 { 61547 if (pLength == NULL) { 61548 return MA_INVALID_ARGS; 61549 } 61550 61551 *pLength = 0; /* Safety. */ 61552 61553 if (pWav == NULL) { 61554 return MA_INVALID_ARGS; 61555 } 61556 61557 #if !defined(MA_NO_WAV) 61558 { 61559 ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); 61560 if (wavResult != MA_SUCCESS) { 61561 return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ 61562 } 61563 61564 return MA_SUCCESS; 61565 } 61566 #else 61567 { 61568 /* wav is disabled. Should never hit this since initialization would have failed. */ 61569 MA_ASSERT(MA_FALSE); 61570 return MA_NOT_IMPLEMENTED; 61571 } 61572 #endif 61573 } 61574 61575 61576 static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 61577 { 61578 ma_result result; 61579 ma_wav* pWav; 61580 61581 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 61582 61583 /* For now we're just allocating the decoder backend on the heap. */ 61584 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); 61585 if (pWav == NULL) { 61586 return MA_OUT_OF_MEMORY; 61587 } 61588 61589 result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); 61590 if (result != MA_SUCCESS) { 61591 ma_free(pWav, pAllocationCallbacks); 61592 return result; 61593 } 61594 61595 *ppBackend = pWav; 61596 61597 return MA_SUCCESS; 61598 } 61599 61600 static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 61601 { 61602 ma_result result; 61603 ma_wav* pWav; 61604 61605 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 61606 61607 /* For now we're just allocating the decoder backend on the heap. */ 61608 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); 61609 if (pWav == NULL) { 61610 return MA_OUT_OF_MEMORY; 61611 } 61612 61613 result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); 61614 if (result != MA_SUCCESS) { 61615 ma_free(pWav, pAllocationCallbacks); 61616 return result; 61617 } 61618 61619 *ppBackend = pWav; 61620 61621 return MA_SUCCESS; 61622 } 61623 61624 static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 61625 { 61626 ma_result result; 61627 ma_wav* pWav; 61628 61629 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 61630 61631 /* For now we're just allocating the decoder backend on the heap. */ 61632 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); 61633 if (pWav == NULL) { 61634 return MA_OUT_OF_MEMORY; 61635 } 61636 61637 result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); 61638 if (result != MA_SUCCESS) { 61639 ma_free(pWav, pAllocationCallbacks); 61640 return result; 61641 } 61642 61643 *ppBackend = pWav; 61644 61645 return MA_SUCCESS; 61646 } 61647 61648 static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 61649 { 61650 ma_result result; 61651 ma_wav* pWav; 61652 61653 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 61654 61655 /* For now we're just allocating the decoder backend on the heap. */ 61656 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); 61657 if (pWav == NULL) { 61658 return MA_OUT_OF_MEMORY; 61659 } 61660 61661 result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); 61662 if (result != MA_SUCCESS) { 61663 ma_free(pWav, pAllocationCallbacks); 61664 return result; 61665 } 61666 61667 *ppBackend = pWav; 61668 61669 return MA_SUCCESS; 61670 } 61671 61672 static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) 61673 { 61674 ma_wav* pWav = (ma_wav*)pBackend; 61675 61676 (void)pUserData; 61677 61678 ma_wav_uninit(pWav, pAllocationCallbacks); 61679 ma_free(pWav, pAllocationCallbacks); 61680 } 61681 61682 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = 61683 { 61684 ma_decoding_backend_init__wav, 61685 ma_decoding_backend_init_file__wav, 61686 ma_decoding_backend_init_file_w__wav, 61687 ma_decoding_backend_init_memory__wav, 61688 ma_decoding_backend_uninit__wav 61689 }; 61690 61691 static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 61692 { 61693 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); 61694 } 61695 61696 static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 61697 { 61698 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); 61699 } 61700 61701 static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 61702 { 61703 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); 61704 } 61705 61706 static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 61707 { 61708 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); 61709 } 61710 #endif /* ma_dr_wav_h */ 61711 61712 /* FLAC */ 61713 #ifdef ma_dr_flac_h 61714 #define MA_HAS_FLAC 61715 61716 typedef struct 61717 { 61718 ma_data_source_base ds; 61719 ma_read_proc onRead; 61720 ma_seek_proc onSeek; 61721 ma_tell_proc onTell; 61722 void* pReadSeekTellUserData; 61723 ma_format format; /* Can be f32, s16 or s32. */ 61724 #if !defined(MA_NO_FLAC) 61725 ma_dr_flac* dr; 61726 #endif 61727 } ma_flac; 61728 61729 MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); 61730 MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); 61731 MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); 61732 MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); 61733 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); 61734 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 61735 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); 61736 MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 61737 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); 61738 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); 61739 61740 61741 static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 61742 { 61743 return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); 61744 } 61745 61746 static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 61747 { 61748 return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); 61749 } 61750 61751 static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 61752 { 61753 return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 61754 } 61755 61756 static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 61757 { 61758 return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); 61759 } 61760 61761 static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 61762 { 61763 return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); 61764 } 61765 61766 static ma_data_source_vtable g_ma_flac_ds_vtable = 61767 { 61768 ma_flac_ds_read, 61769 ma_flac_ds_seek, 61770 ma_flac_ds_get_data_format, 61771 ma_flac_ds_get_cursor, 61772 ma_flac_ds_get_length, 61773 NULL, /* onSetLooping */ 61774 0 61775 }; 61776 61777 61778 #if !defined(MA_NO_FLAC) 61779 static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) 61780 { 61781 ma_flac* pFlac = (ma_flac*)pUserData; 61782 ma_result result; 61783 size_t bytesRead; 61784 61785 MA_ASSERT(pFlac != NULL); 61786 61787 result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); 61788 (void)result; 61789 61790 return bytesRead; 61791 } 61792 61793 static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) 61794 { 61795 ma_flac* pFlac = (ma_flac*)pUserData; 61796 ma_result result; 61797 ma_seek_origin maSeekOrigin; 61798 61799 MA_ASSERT(pFlac != NULL); 61800 61801 maSeekOrigin = ma_seek_origin_start; 61802 if (origin == ma_dr_flac_seek_origin_current) { 61803 maSeekOrigin = ma_seek_origin_current; 61804 } 61805 61806 result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); 61807 if (result != MA_SUCCESS) { 61808 return MA_FALSE; 61809 } 61810 61811 return MA_TRUE; 61812 } 61813 #endif 61814 61815 static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) 61816 { 61817 ma_result result; 61818 ma_data_source_config dataSourceConfig; 61819 61820 if (pFlac == NULL) { 61821 return MA_INVALID_ARGS; 61822 } 61823 61824 MA_ZERO_OBJECT(pFlac); 61825 pFlac->format = ma_format_f32; /* f32 by default. */ 61826 61827 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { 61828 pFlac->format = pConfig->preferredFormat; 61829 } else { 61830 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ 61831 } 61832 61833 dataSourceConfig = ma_data_source_config_init(); 61834 dataSourceConfig.vtable = &g_ma_flac_ds_vtable; 61835 61836 result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); 61837 if (result != MA_SUCCESS) { 61838 return result; /* Failed to initialize the base data source. */ 61839 } 61840 61841 return MA_SUCCESS; 61842 } 61843 61844 MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) 61845 { 61846 ma_result result; 61847 61848 result = ma_flac_init_internal(pConfig, pFlac); 61849 if (result != MA_SUCCESS) { 61850 return result; 61851 } 61852 61853 if (onRead == NULL || onSeek == NULL) { 61854 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ 61855 } 61856 61857 pFlac->onRead = onRead; 61858 pFlac->onSeek = onSeek; 61859 pFlac->onTell = onTell; 61860 pFlac->pReadSeekTellUserData = pReadSeekTellUserData; 61861 61862 #if !defined(MA_NO_FLAC) 61863 { 61864 pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); 61865 if (pFlac->dr == NULL) { 61866 return MA_INVALID_FILE; 61867 } 61868 61869 return MA_SUCCESS; 61870 } 61871 #else 61872 { 61873 /* flac is disabled. */ 61874 (void)pAllocationCallbacks; 61875 return MA_NOT_IMPLEMENTED; 61876 } 61877 #endif 61878 } 61879 61880 MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) 61881 { 61882 ma_result result; 61883 61884 result = ma_flac_init_internal(pConfig, pFlac); 61885 if (result != MA_SUCCESS) { 61886 return result; 61887 } 61888 61889 #if !defined(MA_NO_FLAC) 61890 { 61891 pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); 61892 if (pFlac->dr == NULL) { 61893 return MA_INVALID_FILE; 61894 } 61895 61896 return MA_SUCCESS; 61897 } 61898 #else 61899 { 61900 /* flac is disabled. */ 61901 (void)pFilePath; 61902 (void)pAllocationCallbacks; 61903 return MA_NOT_IMPLEMENTED; 61904 } 61905 #endif 61906 } 61907 61908 MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) 61909 { 61910 ma_result result; 61911 61912 result = ma_flac_init_internal(pConfig, pFlac); 61913 if (result != MA_SUCCESS) { 61914 return result; 61915 } 61916 61917 #if !defined(MA_NO_FLAC) 61918 { 61919 pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); 61920 if (pFlac->dr == NULL) { 61921 return MA_INVALID_FILE; 61922 } 61923 61924 return MA_SUCCESS; 61925 } 61926 #else 61927 { 61928 /* flac is disabled. */ 61929 (void)pFilePath; 61930 (void)pAllocationCallbacks; 61931 return MA_NOT_IMPLEMENTED; 61932 } 61933 #endif 61934 } 61935 61936 MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) 61937 { 61938 ma_result result; 61939 61940 result = ma_flac_init_internal(pConfig, pFlac); 61941 if (result != MA_SUCCESS) { 61942 return result; 61943 } 61944 61945 #if !defined(MA_NO_FLAC) 61946 { 61947 pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); 61948 if (pFlac->dr == NULL) { 61949 return MA_INVALID_FILE; 61950 } 61951 61952 return MA_SUCCESS; 61953 } 61954 #else 61955 { 61956 /* flac is disabled. */ 61957 (void)pData; 61958 (void)dataSize; 61959 (void)pAllocationCallbacks; 61960 return MA_NOT_IMPLEMENTED; 61961 } 61962 #endif 61963 } 61964 61965 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) 61966 { 61967 if (pFlac == NULL) { 61968 return; 61969 } 61970 61971 (void)pAllocationCallbacks; 61972 61973 #if !defined(MA_NO_FLAC) 61974 { 61975 ma_dr_flac_close(pFlac->dr); 61976 } 61977 #else 61978 { 61979 /* flac is disabled. Should never hit this since initialization would have failed. */ 61980 MA_ASSERT(MA_FALSE); 61981 } 61982 #endif 61983 61984 ma_data_source_uninit(&pFlac->ds); 61985 } 61986 61987 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 61988 { 61989 if (pFramesRead != NULL) { 61990 *pFramesRead = 0; 61991 } 61992 61993 if (frameCount == 0) { 61994 return MA_INVALID_ARGS; 61995 } 61996 61997 if (pFlac == NULL) { 61998 return MA_INVALID_ARGS; 61999 } 62000 62001 #if !defined(MA_NO_FLAC) 62002 { 62003 /* We always use floating point format. */ 62004 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ 62005 ma_uint64 totalFramesRead = 0; 62006 ma_format format; 62007 62008 ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); 62009 62010 switch (format) 62011 { 62012 case ma_format_f32: 62013 { 62014 totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); 62015 } break; 62016 62017 case ma_format_s16: 62018 { 62019 totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); 62020 } break; 62021 62022 case ma_format_s32: 62023 { 62024 totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); 62025 } break; 62026 62027 case ma_format_u8: 62028 case ma_format_s24: 62029 case ma_format_unknown: 62030 default: 62031 { 62032 return MA_INVALID_OPERATION; 62033 }; 62034 } 62035 62036 /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ 62037 if (totalFramesRead == 0) { 62038 result = MA_AT_END; 62039 } 62040 62041 if (pFramesRead != NULL) { 62042 *pFramesRead = totalFramesRead; 62043 } 62044 62045 if (result == MA_SUCCESS && totalFramesRead == 0) { 62046 result = MA_AT_END; 62047 } 62048 62049 return result; 62050 } 62051 #else 62052 { 62053 /* flac is disabled. Should never hit this since initialization would have failed. */ 62054 MA_ASSERT(MA_FALSE); 62055 62056 (void)pFramesOut; 62057 (void)frameCount; 62058 (void)pFramesRead; 62059 62060 return MA_NOT_IMPLEMENTED; 62061 } 62062 #endif 62063 } 62064 62065 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) 62066 { 62067 if (pFlac == NULL) { 62068 return MA_INVALID_ARGS; 62069 } 62070 62071 #if !defined(MA_NO_FLAC) 62072 { 62073 ma_bool32 flacResult; 62074 62075 flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); 62076 if (flacResult != MA_TRUE) { 62077 return MA_ERROR; 62078 } 62079 62080 return MA_SUCCESS; 62081 } 62082 #else 62083 { 62084 /* flac is disabled. Should never hit this since initialization would have failed. */ 62085 MA_ASSERT(MA_FALSE); 62086 62087 (void)frameIndex; 62088 62089 return MA_NOT_IMPLEMENTED; 62090 } 62091 #endif 62092 } 62093 62094 MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 62095 { 62096 /* Defaults for safety. */ 62097 if (pFormat != NULL) { 62098 *pFormat = ma_format_unknown; 62099 } 62100 if (pChannels != NULL) { 62101 *pChannels = 0; 62102 } 62103 if (pSampleRate != NULL) { 62104 *pSampleRate = 0; 62105 } 62106 if (pChannelMap != NULL) { 62107 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 62108 } 62109 62110 if (pFlac == NULL) { 62111 return MA_INVALID_OPERATION; 62112 } 62113 62114 if (pFormat != NULL) { 62115 *pFormat = pFlac->format; 62116 } 62117 62118 #if !defined(MA_NO_FLAC) 62119 { 62120 if (pChannels != NULL) { 62121 *pChannels = pFlac->dr->channels; 62122 } 62123 62124 if (pSampleRate != NULL) { 62125 *pSampleRate = pFlac->dr->sampleRate; 62126 } 62127 62128 if (pChannelMap != NULL) { 62129 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); 62130 } 62131 62132 return MA_SUCCESS; 62133 } 62134 #else 62135 { 62136 /* flac is disabled. Should never hit this since initialization would have failed. */ 62137 MA_ASSERT(MA_FALSE); 62138 return MA_NOT_IMPLEMENTED; 62139 } 62140 #endif 62141 } 62142 62143 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) 62144 { 62145 if (pCursor == NULL) { 62146 return MA_INVALID_ARGS; 62147 } 62148 62149 *pCursor = 0; /* Safety. */ 62150 62151 if (pFlac == NULL) { 62152 return MA_INVALID_ARGS; 62153 } 62154 62155 #if !defined(MA_NO_FLAC) 62156 { 62157 *pCursor = pFlac->dr->currentPCMFrame; 62158 62159 return MA_SUCCESS; 62160 } 62161 #else 62162 { 62163 /* flac is disabled. Should never hit this since initialization would have failed. */ 62164 MA_ASSERT(MA_FALSE); 62165 return MA_NOT_IMPLEMENTED; 62166 } 62167 #endif 62168 } 62169 62170 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) 62171 { 62172 if (pLength == NULL) { 62173 return MA_INVALID_ARGS; 62174 } 62175 62176 *pLength = 0; /* Safety. */ 62177 62178 if (pFlac == NULL) { 62179 return MA_INVALID_ARGS; 62180 } 62181 62182 #if !defined(MA_NO_FLAC) 62183 { 62184 *pLength = pFlac->dr->totalPCMFrameCount; 62185 62186 return MA_SUCCESS; 62187 } 62188 #else 62189 { 62190 /* flac is disabled. Should never hit this since initialization would have failed. */ 62191 MA_ASSERT(MA_FALSE); 62192 return MA_NOT_IMPLEMENTED; 62193 } 62194 #endif 62195 } 62196 62197 62198 static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62199 { 62200 ma_result result; 62201 ma_flac* pFlac; 62202 62203 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62204 62205 /* For now we're just allocating the decoder backend on the heap. */ 62206 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); 62207 if (pFlac == NULL) { 62208 return MA_OUT_OF_MEMORY; 62209 } 62210 62211 result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); 62212 if (result != MA_SUCCESS) { 62213 ma_free(pFlac, pAllocationCallbacks); 62214 return result; 62215 } 62216 62217 *ppBackend = pFlac; 62218 62219 return MA_SUCCESS; 62220 } 62221 62222 static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62223 { 62224 ma_result result; 62225 ma_flac* pFlac; 62226 62227 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62228 62229 /* For now we're just allocating the decoder backend on the heap. */ 62230 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); 62231 if (pFlac == NULL) { 62232 return MA_OUT_OF_MEMORY; 62233 } 62234 62235 result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); 62236 if (result != MA_SUCCESS) { 62237 ma_free(pFlac, pAllocationCallbacks); 62238 return result; 62239 } 62240 62241 *ppBackend = pFlac; 62242 62243 return MA_SUCCESS; 62244 } 62245 62246 static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62247 { 62248 ma_result result; 62249 ma_flac* pFlac; 62250 62251 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62252 62253 /* For now we're just allocating the decoder backend on the heap. */ 62254 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); 62255 if (pFlac == NULL) { 62256 return MA_OUT_OF_MEMORY; 62257 } 62258 62259 result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); 62260 if (result != MA_SUCCESS) { 62261 ma_free(pFlac, pAllocationCallbacks); 62262 return result; 62263 } 62264 62265 *ppBackend = pFlac; 62266 62267 return MA_SUCCESS; 62268 } 62269 62270 static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62271 { 62272 ma_result result; 62273 ma_flac* pFlac; 62274 62275 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62276 62277 /* For now we're just allocating the decoder backend on the heap. */ 62278 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); 62279 if (pFlac == NULL) { 62280 return MA_OUT_OF_MEMORY; 62281 } 62282 62283 result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); 62284 if (result != MA_SUCCESS) { 62285 ma_free(pFlac, pAllocationCallbacks); 62286 return result; 62287 } 62288 62289 *ppBackend = pFlac; 62290 62291 return MA_SUCCESS; 62292 } 62293 62294 static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) 62295 { 62296 ma_flac* pFlac = (ma_flac*)pBackend; 62297 62298 (void)pUserData; 62299 62300 ma_flac_uninit(pFlac, pAllocationCallbacks); 62301 ma_free(pFlac, pAllocationCallbacks); 62302 } 62303 62304 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = 62305 { 62306 ma_decoding_backend_init__flac, 62307 ma_decoding_backend_init_file__flac, 62308 ma_decoding_backend_init_file_w__flac, 62309 ma_decoding_backend_init_memory__flac, 62310 ma_decoding_backend_uninit__flac 62311 }; 62312 62313 static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62314 { 62315 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); 62316 } 62317 62318 static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62319 { 62320 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); 62321 } 62322 62323 static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62324 { 62325 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); 62326 } 62327 62328 static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62329 { 62330 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); 62331 } 62332 #endif /* ma_dr_flac_h */ 62333 62334 /* MP3 */ 62335 #ifdef ma_dr_mp3_h 62336 #define MA_HAS_MP3 62337 62338 typedef struct 62339 { 62340 ma_data_source_base ds; 62341 ma_read_proc onRead; 62342 ma_seek_proc onSeek; 62343 ma_tell_proc onTell; 62344 void* pReadSeekTellUserData; 62345 ma_format format; /* Can be f32 or s16. */ 62346 #if !defined(MA_NO_MP3) 62347 ma_dr_mp3 dr; 62348 ma_uint32 seekPointCount; 62349 ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ 62350 #endif 62351 } ma_mp3; 62352 62353 MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); 62354 MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); 62355 MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); 62356 MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); 62357 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); 62358 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 62359 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); 62360 MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 62361 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); 62362 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); 62363 62364 62365 static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 62366 { 62367 return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); 62368 } 62369 62370 static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 62371 { 62372 return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); 62373 } 62374 62375 static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 62376 { 62377 return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 62378 } 62379 62380 static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 62381 { 62382 return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); 62383 } 62384 62385 static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 62386 { 62387 return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); 62388 } 62389 62390 static ma_data_source_vtable g_ma_mp3_ds_vtable = 62391 { 62392 ma_mp3_ds_read, 62393 ma_mp3_ds_seek, 62394 ma_mp3_ds_get_data_format, 62395 ma_mp3_ds_get_cursor, 62396 ma_mp3_ds_get_length, 62397 NULL, /* onSetLooping */ 62398 0 62399 }; 62400 62401 62402 #if !defined(MA_NO_MP3) 62403 static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) 62404 { 62405 ma_mp3* pMP3 = (ma_mp3*)pUserData; 62406 ma_result result; 62407 size_t bytesRead; 62408 62409 MA_ASSERT(pMP3 != NULL); 62410 62411 result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); 62412 (void)result; 62413 62414 return bytesRead; 62415 } 62416 62417 static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) 62418 { 62419 ma_mp3* pMP3 = (ma_mp3*)pUserData; 62420 ma_result result; 62421 ma_seek_origin maSeekOrigin; 62422 62423 MA_ASSERT(pMP3 != NULL); 62424 62425 maSeekOrigin = ma_seek_origin_start; 62426 if (origin == ma_dr_mp3_seek_origin_current) { 62427 maSeekOrigin = ma_seek_origin_current; 62428 } 62429 62430 result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); 62431 if (result != MA_SUCCESS) { 62432 return MA_FALSE; 62433 } 62434 62435 return MA_TRUE; 62436 } 62437 #endif 62438 62439 static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) 62440 { 62441 ma_result result; 62442 ma_data_source_config dataSourceConfig; 62443 62444 if (pMP3 == NULL) { 62445 return MA_INVALID_ARGS; 62446 } 62447 62448 MA_ZERO_OBJECT(pMP3); 62449 pMP3->format = ma_format_f32; /* f32 by default. */ 62450 62451 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { 62452 pMP3->format = pConfig->preferredFormat; 62453 } else { 62454 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ 62455 } 62456 62457 dataSourceConfig = ma_data_source_config_init(); 62458 dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; 62459 62460 result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); 62461 if (result != MA_SUCCESS) { 62462 return result; /* Failed to initialize the base data source. */ 62463 } 62464 62465 return MA_SUCCESS; 62466 } 62467 62468 static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) 62469 { 62470 ma_bool32 mp3Result; 62471 ma_uint32 seekPointCount = 0; 62472 ma_dr_mp3_seek_point* pSeekPoints = NULL; 62473 62474 MA_ASSERT(pMP3 != NULL); 62475 MA_ASSERT(pConfig != NULL); 62476 62477 seekPointCount = pConfig->seekPointCount; 62478 if (seekPointCount > 0) { 62479 pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); 62480 if (pSeekPoints == NULL) { 62481 return MA_OUT_OF_MEMORY; 62482 } 62483 } 62484 62485 mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); 62486 if (mp3Result != MA_TRUE) { 62487 ma_free(pSeekPoints, pAllocationCallbacks); 62488 return MA_ERROR; 62489 } 62490 62491 mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); 62492 if (mp3Result != MA_TRUE) { 62493 ma_free(pSeekPoints, pAllocationCallbacks); 62494 return MA_ERROR; 62495 } 62496 62497 pMP3->seekPointCount = seekPointCount; 62498 pMP3->pSeekPoints = pSeekPoints; 62499 62500 return MA_SUCCESS; 62501 } 62502 62503 static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) 62504 { 62505 ma_result result; 62506 62507 result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); 62508 if (result != MA_SUCCESS) { 62509 return result; 62510 } 62511 62512 return MA_SUCCESS; 62513 } 62514 62515 MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) 62516 { 62517 ma_result result; 62518 62519 result = ma_mp3_init_internal(pConfig, pMP3); 62520 if (result != MA_SUCCESS) { 62521 return result; 62522 } 62523 62524 if (onRead == NULL || onSeek == NULL) { 62525 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ 62526 } 62527 62528 pMP3->onRead = onRead; 62529 pMP3->onSeek = onSeek; 62530 pMP3->onTell = onTell; 62531 pMP3->pReadSeekTellUserData = pReadSeekTellUserData; 62532 62533 #if !defined(MA_NO_MP3) 62534 { 62535 ma_bool32 mp3Result; 62536 62537 mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); 62538 if (mp3Result != MA_TRUE) { 62539 return MA_INVALID_FILE; 62540 } 62541 62542 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); 62543 62544 return MA_SUCCESS; 62545 } 62546 #else 62547 { 62548 /* mp3 is disabled. */ 62549 (void)pAllocationCallbacks; 62550 return MA_NOT_IMPLEMENTED; 62551 } 62552 #endif 62553 } 62554 62555 MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) 62556 { 62557 ma_result result; 62558 62559 result = ma_mp3_init_internal(pConfig, pMP3); 62560 if (result != MA_SUCCESS) { 62561 return result; 62562 } 62563 62564 #if !defined(MA_NO_MP3) 62565 { 62566 ma_bool32 mp3Result; 62567 62568 mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); 62569 if (mp3Result != MA_TRUE) { 62570 return MA_INVALID_FILE; 62571 } 62572 62573 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); 62574 62575 return MA_SUCCESS; 62576 } 62577 #else 62578 { 62579 /* mp3 is disabled. */ 62580 (void)pFilePath; 62581 (void)pAllocationCallbacks; 62582 return MA_NOT_IMPLEMENTED; 62583 } 62584 #endif 62585 } 62586 62587 MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) 62588 { 62589 ma_result result; 62590 62591 result = ma_mp3_init_internal(pConfig, pMP3); 62592 if (result != MA_SUCCESS) { 62593 return result; 62594 } 62595 62596 #if !defined(MA_NO_MP3) 62597 { 62598 ma_bool32 mp3Result; 62599 62600 mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); 62601 if (mp3Result != MA_TRUE) { 62602 return MA_INVALID_FILE; 62603 } 62604 62605 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); 62606 62607 return MA_SUCCESS; 62608 } 62609 #else 62610 { 62611 /* mp3 is disabled. */ 62612 (void)pFilePath; 62613 (void)pAllocationCallbacks; 62614 return MA_NOT_IMPLEMENTED; 62615 } 62616 #endif 62617 } 62618 62619 MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) 62620 { 62621 ma_result result; 62622 62623 result = ma_mp3_init_internal(pConfig, pMP3); 62624 if (result != MA_SUCCESS) { 62625 return result; 62626 } 62627 62628 #if !defined(MA_NO_MP3) 62629 { 62630 ma_bool32 mp3Result; 62631 62632 mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); 62633 if (mp3Result != MA_TRUE) { 62634 return MA_INVALID_FILE; 62635 } 62636 62637 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); 62638 62639 return MA_SUCCESS; 62640 } 62641 #else 62642 { 62643 /* mp3 is disabled. */ 62644 (void)pData; 62645 (void)dataSize; 62646 (void)pAllocationCallbacks; 62647 return MA_NOT_IMPLEMENTED; 62648 } 62649 #endif 62650 } 62651 62652 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) 62653 { 62654 if (pMP3 == NULL) { 62655 return; 62656 } 62657 62658 #if !defined(MA_NO_MP3) 62659 { 62660 ma_dr_mp3_uninit(&pMP3->dr); 62661 } 62662 #else 62663 { 62664 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62665 MA_ASSERT(MA_FALSE); 62666 } 62667 #endif 62668 62669 /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ 62670 ma_free(pMP3->pSeekPoints, pAllocationCallbacks); 62671 62672 ma_data_source_uninit(&pMP3->ds); 62673 } 62674 62675 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 62676 { 62677 if (pFramesRead != NULL) { 62678 *pFramesRead = 0; 62679 } 62680 62681 if (frameCount == 0) { 62682 return MA_INVALID_ARGS; 62683 } 62684 62685 if (pMP3 == NULL) { 62686 return MA_INVALID_ARGS; 62687 } 62688 62689 #if !defined(MA_NO_MP3) 62690 { 62691 /* We always use floating point format. */ 62692 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ 62693 ma_uint64 totalFramesRead = 0; 62694 ma_format format; 62695 62696 ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); 62697 62698 switch (format) 62699 { 62700 case ma_format_f32: 62701 { 62702 totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); 62703 } break; 62704 62705 case ma_format_s16: 62706 { 62707 totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); 62708 } break; 62709 62710 case ma_format_u8: 62711 case ma_format_s24: 62712 case ma_format_s32: 62713 case ma_format_unknown: 62714 default: 62715 { 62716 return MA_INVALID_OPERATION; 62717 }; 62718 } 62719 62720 /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ 62721 if (totalFramesRead == 0) { 62722 result = MA_AT_END; 62723 } 62724 62725 if (pFramesRead != NULL) { 62726 *pFramesRead = totalFramesRead; 62727 } 62728 62729 return result; 62730 } 62731 #else 62732 { 62733 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62734 MA_ASSERT(MA_FALSE); 62735 62736 (void)pFramesOut; 62737 (void)frameCount; 62738 (void)pFramesRead; 62739 62740 return MA_NOT_IMPLEMENTED; 62741 } 62742 #endif 62743 } 62744 62745 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) 62746 { 62747 if (pMP3 == NULL) { 62748 return MA_INVALID_ARGS; 62749 } 62750 62751 #if !defined(MA_NO_MP3) 62752 { 62753 ma_bool32 mp3Result; 62754 62755 mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); 62756 if (mp3Result != MA_TRUE) { 62757 return MA_ERROR; 62758 } 62759 62760 return MA_SUCCESS; 62761 } 62762 #else 62763 { 62764 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62765 MA_ASSERT(MA_FALSE); 62766 62767 (void)frameIndex; 62768 62769 return MA_NOT_IMPLEMENTED; 62770 } 62771 #endif 62772 } 62773 62774 MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 62775 { 62776 /* Defaults for safety. */ 62777 if (pFormat != NULL) { 62778 *pFormat = ma_format_unknown; 62779 } 62780 if (pChannels != NULL) { 62781 *pChannels = 0; 62782 } 62783 if (pSampleRate != NULL) { 62784 *pSampleRate = 0; 62785 } 62786 if (pChannelMap != NULL) { 62787 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 62788 } 62789 62790 if (pMP3 == NULL) { 62791 return MA_INVALID_OPERATION; 62792 } 62793 62794 if (pFormat != NULL) { 62795 *pFormat = pMP3->format; 62796 } 62797 62798 #if !defined(MA_NO_MP3) 62799 { 62800 if (pChannels != NULL) { 62801 *pChannels = pMP3->dr.channels; 62802 } 62803 62804 if (pSampleRate != NULL) { 62805 *pSampleRate = pMP3->dr.sampleRate; 62806 } 62807 62808 if (pChannelMap != NULL) { 62809 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); 62810 } 62811 62812 return MA_SUCCESS; 62813 } 62814 #else 62815 { 62816 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62817 MA_ASSERT(MA_FALSE); 62818 return MA_NOT_IMPLEMENTED; 62819 } 62820 #endif 62821 } 62822 62823 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) 62824 { 62825 if (pCursor == NULL) { 62826 return MA_INVALID_ARGS; 62827 } 62828 62829 *pCursor = 0; /* Safety. */ 62830 62831 if (pMP3 == NULL) { 62832 return MA_INVALID_ARGS; 62833 } 62834 62835 #if !defined(MA_NO_MP3) 62836 { 62837 *pCursor = pMP3->dr.currentPCMFrame; 62838 62839 return MA_SUCCESS; 62840 } 62841 #else 62842 { 62843 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62844 MA_ASSERT(MA_FALSE); 62845 return MA_NOT_IMPLEMENTED; 62846 } 62847 #endif 62848 } 62849 62850 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) 62851 { 62852 if (pLength == NULL) { 62853 return MA_INVALID_ARGS; 62854 } 62855 62856 *pLength = 0; /* Safety. */ 62857 62858 if (pMP3 == NULL) { 62859 return MA_INVALID_ARGS; 62860 } 62861 62862 #if !defined(MA_NO_MP3) 62863 { 62864 *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); 62865 62866 return MA_SUCCESS; 62867 } 62868 #else 62869 { 62870 /* mp3 is disabled. Should never hit this since initialization would have failed. */ 62871 MA_ASSERT(MA_FALSE); 62872 return MA_NOT_IMPLEMENTED; 62873 } 62874 #endif 62875 } 62876 62877 62878 static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62879 { 62880 ma_result result; 62881 ma_mp3* pMP3; 62882 62883 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62884 62885 /* For now we're just allocating the decoder backend on the heap. */ 62886 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); 62887 if (pMP3 == NULL) { 62888 return MA_OUT_OF_MEMORY; 62889 } 62890 62891 result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); 62892 if (result != MA_SUCCESS) { 62893 ma_free(pMP3, pAllocationCallbacks); 62894 return result; 62895 } 62896 62897 *ppBackend = pMP3; 62898 62899 return MA_SUCCESS; 62900 } 62901 62902 static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62903 { 62904 ma_result result; 62905 ma_mp3* pMP3; 62906 62907 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62908 62909 /* For now we're just allocating the decoder backend on the heap. */ 62910 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); 62911 if (pMP3 == NULL) { 62912 return MA_OUT_OF_MEMORY; 62913 } 62914 62915 result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); 62916 if (result != MA_SUCCESS) { 62917 ma_free(pMP3, pAllocationCallbacks); 62918 return result; 62919 } 62920 62921 *ppBackend = pMP3; 62922 62923 return MA_SUCCESS; 62924 } 62925 62926 static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62927 { 62928 ma_result result; 62929 ma_mp3* pMP3; 62930 62931 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62932 62933 /* For now we're just allocating the decoder backend on the heap. */ 62934 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); 62935 if (pMP3 == NULL) { 62936 return MA_OUT_OF_MEMORY; 62937 } 62938 62939 result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); 62940 if (result != MA_SUCCESS) { 62941 ma_free(pMP3, pAllocationCallbacks); 62942 return result; 62943 } 62944 62945 *ppBackend = pMP3; 62946 62947 return MA_SUCCESS; 62948 } 62949 62950 static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 62951 { 62952 ma_result result; 62953 ma_mp3* pMP3; 62954 62955 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 62956 62957 /* For now we're just allocating the decoder backend on the heap. */ 62958 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); 62959 if (pMP3 == NULL) { 62960 return MA_OUT_OF_MEMORY; 62961 } 62962 62963 result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); 62964 if (result != MA_SUCCESS) { 62965 ma_free(pMP3, pAllocationCallbacks); 62966 return result; 62967 } 62968 62969 *ppBackend = pMP3; 62970 62971 return MA_SUCCESS; 62972 } 62973 62974 static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) 62975 { 62976 ma_mp3* pMP3 = (ma_mp3*)pBackend; 62977 62978 (void)pUserData; 62979 62980 ma_mp3_uninit(pMP3, pAllocationCallbacks); 62981 ma_free(pMP3, pAllocationCallbacks); 62982 } 62983 62984 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = 62985 { 62986 ma_decoding_backend_init__mp3, 62987 ma_decoding_backend_init_file__mp3, 62988 ma_decoding_backend_init_file_w__mp3, 62989 ma_decoding_backend_init_memory__mp3, 62990 ma_decoding_backend_uninit__mp3 62991 }; 62992 62993 static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62994 { 62995 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); 62996 } 62997 62998 static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 62999 { 63000 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); 63001 } 63002 63003 static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63004 { 63005 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); 63006 } 63007 63008 static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63009 { 63010 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); 63011 } 63012 #endif /* ma_dr_mp3_h */ 63013 63014 /* Vorbis */ 63015 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H 63016 #define MA_HAS_VORBIS 63017 63018 /* The size in bytes of each chunk of data to read from the Vorbis stream. */ 63019 #define MA_VORBIS_DATA_CHUNK_SIZE 4096 63020 63021 typedef struct 63022 { 63023 ma_data_source_base ds; 63024 ma_read_proc onRead; 63025 ma_seek_proc onSeek; 63026 ma_tell_proc onTell; 63027 void* pReadSeekTellUserData; 63028 ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ 63029 ma_format format; /* Only f32 is allowed with stb_vorbis. */ 63030 ma_uint32 channels; 63031 ma_uint32 sampleRate; 63032 ma_uint64 cursor; 63033 #if !defined(MA_NO_VORBIS) 63034 stb_vorbis* stb; 63035 ma_bool32 usingPushMode; 63036 struct 63037 { 63038 ma_uint8* pData; 63039 size_t dataSize; 63040 size_t dataCapacity; 63041 size_t audioStartOffsetInBytes; 63042 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ 63043 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ 63044 float** ppPacketData; 63045 } push; 63046 #endif 63047 } ma_stbvorbis; 63048 63049 MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); 63050 MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); 63051 MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); 63052 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); 63053 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); 63054 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); 63055 MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); 63056 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); 63057 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); 63058 63059 63060 static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 63061 { 63062 return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); 63063 } 63064 63065 static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 63066 { 63067 return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); 63068 } 63069 63070 static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 63071 { 63072 return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 63073 } 63074 63075 static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 63076 { 63077 return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); 63078 } 63079 63080 static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 63081 { 63082 return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); 63083 } 63084 63085 static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = 63086 { 63087 ma_stbvorbis_ds_read, 63088 ma_stbvorbis_ds_seek, 63089 ma_stbvorbis_ds_get_data_format, 63090 ma_stbvorbis_ds_get_cursor, 63091 ma_stbvorbis_ds_get_length, 63092 NULL, /* onSetLooping */ 63093 0 63094 }; 63095 63096 63097 static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) 63098 { 63099 ma_result result; 63100 ma_data_source_config dataSourceConfig; 63101 63102 (void)pConfig; 63103 63104 if (pVorbis == NULL) { 63105 return MA_INVALID_ARGS; 63106 } 63107 63108 MA_ZERO_OBJECT(pVorbis); 63109 pVorbis->format = ma_format_f32; /* Only supporting f32. */ 63110 63111 dataSourceConfig = ma_data_source_config_init(); 63112 dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; 63113 63114 result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); 63115 if (result != MA_SUCCESS) { 63116 return result; /* Failed to initialize the base data source. */ 63117 } 63118 63119 return MA_SUCCESS; 63120 } 63121 63122 #if !defined(MA_NO_VORBIS) 63123 static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) 63124 { 63125 stb_vorbis_info info; 63126 63127 MA_ASSERT(pVorbis != NULL); 63128 63129 info = stb_vorbis_get_info(pVorbis->stb); 63130 63131 pVorbis->channels = info.channels; 63132 pVorbis->sampleRate = info.sample_rate; 63133 63134 return MA_SUCCESS; 63135 } 63136 63137 static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) 63138 { 63139 ma_result result; 63140 stb_vorbis* stb; 63141 size_t dataSize = 0; 63142 size_t dataCapacity = 0; 63143 ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ 63144 63145 for (;;) { 63146 int vorbisError; 63147 int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ 63148 size_t bytesRead; 63149 ma_uint8* pNewData; 63150 63151 /* Allocate memory for the new chunk. */ 63152 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; 63153 pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); 63154 if (pNewData == NULL) { 63155 ma_free(pData, &pVorbis->allocationCallbacks); 63156 return MA_OUT_OF_MEMORY; 63157 } 63158 63159 pData = pNewData; 63160 63161 /* Read in the next chunk. */ 63162 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); 63163 dataSize += bytesRead; 63164 63165 if (result != MA_SUCCESS) { 63166 ma_free(pData, &pVorbis->allocationCallbacks); 63167 return result; 63168 } 63169 63170 /* We have a maximum of 31 bits with stb_vorbis. */ 63171 if (dataSize > INT_MAX) { 63172 ma_free(pData, &pVorbis->allocationCallbacks); 63173 return MA_TOO_BIG; 63174 } 63175 63176 stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); 63177 if (stb != NULL) { 63178 /* 63179 Successfully opened the Vorbis decoder. We might have some leftover unprocessed 63180 data so we'll need to move that down to the front. 63181 */ 63182 dataSize -= (size_t)consumedDataSize; /* Consume the data. */ 63183 MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); 63184 63185 /* 63186 We need to track the start point so we can seek back to the start of the audio 63187 data when seeking. 63188 */ 63189 pVorbis->push.audioStartOffsetInBytes = consumedDataSize; 63190 63191 break; 63192 } else { 63193 /* Failed to open the decoder. */ 63194 if (vorbisError == VORBIS_need_more_data) { 63195 continue; 63196 } else { 63197 ma_free(pData, &pVorbis->allocationCallbacks); 63198 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ 63199 } 63200 } 63201 } 63202 63203 MA_ASSERT(stb != NULL); 63204 pVorbis->stb = stb; 63205 pVorbis->push.pData = pData; 63206 pVorbis->push.dataSize = dataSize; 63207 pVorbis->push.dataCapacity = dataCapacity; 63208 63209 return MA_SUCCESS; 63210 } 63211 #endif 63212 63213 MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) 63214 { 63215 ma_result result; 63216 63217 result = ma_stbvorbis_init_internal(pConfig, pVorbis); 63218 if (result != MA_SUCCESS) { 63219 return result; 63220 } 63221 63222 if (onRead == NULL || onSeek == NULL) { 63223 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ 63224 } 63225 63226 pVorbis->onRead = onRead; 63227 pVorbis->onSeek = onSeek; 63228 pVorbis->onTell = onTell; 63229 pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; 63230 ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); 63231 63232 #if !defined(MA_NO_VORBIS) 63233 { 63234 /* 63235 stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the 63236 pushing API. In order for us to be able to successfully initialize the decoder we need to 63237 supply it with enough data. We need to keep loading data until we have enough. 63238 */ 63239 result = ma_stbvorbis_init_internal_decoder_push(pVorbis); 63240 if (result != MA_SUCCESS) { 63241 return result; 63242 } 63243 63244 pVorbis->usingPushMode = MA_TRUE; 63245 63246 result = ma_stbvorbis_post_init(pVorbis); 63247 if (result != MA_SUCCESS) { 63248 stb_vorbis_close(pVorbis->stb); 63249 ma_free(pVorbis->push.pData, pAllocationCallbacks); 63250 return result; 63251 } 63252 63253 return MA_SUCCESS; 63254 } 63255 #else 63256 { 63257 /* vorbis is disabled. */ 63258 (void)pAllocationCallbacks; 63259 return MA_NOT_IMPLEMENTED; 63260 } 63261 #endif 63262 } 63263 63264 MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) 63265 { 63266 ma_result result; 63267 63268 result = ma_stbvorbis_init_internal(pConfig, pVorbis); 63269 if (result != MA_SUCCESS) { 63270 return result; 63271 } 63272 63273 #if !defined(MA_NO_VORBIS) 63274 { 63275 (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ 63276 63277 /* We can use stb_vorbis' pull mode for file based streams. */ 63278 pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); 63279 if (pVorbis->stb == NULL) { 63280 return MA_INVALID_FILE; 63281 } 63282 63283 pVorbis->usingPushMode = MA_FALSE; 63284 63285 result = ma_stbvorbis_post_init(pVorbis); 63286 if (result != MA_SUCCESS) { 63287 stb_vorbis_close(pVorbis->stb); 63288 return result; 63289 } 63290 63291 return MA_SUCCESS; 63292 } 63293 #else 63294 { 63295 /* vorbis is disabled. */ 63296 (void)pFilePath; 63297 (void)pAllocationCallbacks; 63298 return MA_NOT_IMPLEMENTED; 63299 } 63300 #endif 63301 } 63302 63303 MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) 63304 { 63305 ma_result result; 63306 63307 result = ma_stbvorbis_init_internal(pConfig, pVorbis); 63308 if (result != MA_SUCCESS) { 63309 return result; 63310 } 63311 63312 #if !defined(MA_NO_VORBIS) 63313 { 63314 (void)pAllocationCallbacks; 63315 63316 /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ 63317 if (dataSize > INT_MAX) { 63318 return MA_TOO_BIG; 63319 } 63320 63321 pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); 63322 if (pVorbis->stb == NULL) { 63323 return MA_INVALID_FILE; 63324 } 63325 63326 pVorbis->usingPushMode = MA_FALSE; 63327 63328 result = ma_stbvorbis_post_init(pVorbis); 63329 if (result != MA_SUCCESS) { 63330 stb_vorbis_close(pVorbis->stb); 63331 return result; 63332 } 63333 63334 return MA_SUCCESS; 63335 } 63336 #else 63337 { 63338 /* vorbis is disabled. */ 63339 (void)pData; 63340 (void)dataSize; 63341 (void)pAllocationCallbacks; 63342 return MA_NOT_IMPLEMENTED; 63343 } 63344 #endif 63345 } 63346 63347 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) 63348 { 63349 if (pVorbis == NULL) { 63350 return; 63351 } 63352 63353 #if !defined(MA_NO_VORBIS) 63354 { 63355 stb_vorbis_close(pVorbis->stb); 63356 63357 /* We'll have to clear some memory if we're using push mode. */ 63358 if (pVorbis->usingPushMode) { 63359 ma_free(pVorbis->push.pData, pAllocationCallbacks); 63360 } 63361 } 63362 #else 63363 { 63364 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63365 MA_ASSERT(MA_FALSE); 63366 } 63367 #endif 63368 63369 ma_data_source_uninit(&pVorbis->ds); 63370 } 63371 63372 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 63373 { 63374 if (pFramesRead != NULL) { 63375 *pFramesRead = 0; 63376 } 63377 63378 if (frameCount == 0) { 63379 return MA_INVALID_ARGS; 63380 } 63381 63382 if (pVorbis == NULL) { 63383 return MA_INVALID_ARGS; 63384 } 63385 63386 #if !defined(MA_NO_VORBIS) 63387 { 63388 /* We always use floating point format. */ 63389 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ 63390 ma_uint64 totalFramesRead = 0; 63391 ma_format format; 63392 ma_uint32 channels; 63393 63394 ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); 63395 63396 if (format == ma_format_f32) { 63397 /* We read differently depending on whether or not we're using push mode. */ 63398 if (pVorbis->usingPushMode) { 63399 /* Push mode. This is the complex case. */ 63400 float* pFramesOutF32 = (float*)pFramesOut; 63401 63402 while (totalFramesRead < frameCount) { 63403 /* The first thing to do is read from any already-cached frames. */ 63404 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ 63405 63406 /* The output pointer can be null in which case we just treate it as a seek. */ 63407 if (pFramesOut != NULL) { 63408 ma_uint64 iFrame; 63409 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { 63410 ma_uint32 iChannel; 63411 for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { 63412 pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; 63413 } 63414 63415 pFramesOutF32 += pVorbis->channels; 63416 } 63417 } 63418 63419 /* Update pointers and counters. */ 63420 pVorbis->push.framesConsumed += framesToReadFromCache; 63421 pVorbis->push.framesRemaining -= framesToReadFromCache; 63422 totalFramesRead += framesToReadFromCache; 63423 63424 /* Don't bother reading any more frames right now if we've just finished loading. */ 63425 if (totalFramesRead == frameCount) { 63426 break; 63427 } 63428 63429 MA_ASSERT(pVorbis->push.framesRemaining == 0); 63430 63431 /* Getting here means we've run out of cached frames. We'll need to load some more. */ 63432 for (;;) { 63433 int samplesRead = 0; 63434 int consumedDataSize; 63435 63436 /* We need to case dataSize to an int, so make sure we can do it safely. */ 63437 if (pVorbis->push.dataSize > INT_MAX) { 63438 break; /* Too big. */ 63439 } 63440 63441 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); 63442 if (consumedDataSize != 0) { 63443 /* Successfully decoded a Vorbis frame. Consume the data. */ 63444 pVorbis->push.dataSize -= (size_t)consumedDataSize; 63445 MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); 63446 63447 pVorbis->push.framesConsumed = 0; 63448 pVorbis->push.framesRemaining = samplesRead; 63449 63450 break; 63451 } else { 63452 /* Not enough data. Read more. */ 63453 size_t bytesRead; 63454 63455 /* Expand the data buffer if necessary. */ 63456 if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { 63457 size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; 63458 ma_uint8* pNewData; 63459 63460 pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); 63461 if (pNewData == NULL) { 63462 result = MA_OUT_OF_MEMORY; 63463 break; 63464 } 63465 63466 pVorbis->push.pData = pNewData; 63467 pVorbis->push.dataCapacity = newCap; 63468 } 63469 63470 /* We should have enough room to load some data. */ 63471 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); 63472 pVorbis->push.dataSize += bytesRead; 63473 63474 if (result != MA_SUCCESS) { 63475 break; /* Failed to read any data. Get out. */ 63476 } 63477 } 63478 } 63479 63480 /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ 63481 if (result != MA_SUCCESS) { 63482 break; 63483 } 63484 } 63485 } else { 63486 /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ 63487 while (totalFramesRead < frameCount) { 63488 ma_uint64 framesRemaining = (frameCount - totalFramesRead); 63489 int framesRead; 63490 63491 if (framesRemaining > INT_MAX) { 63492 framesRemaining = INT_MAX; 63493 } 63494 63495 framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ 63496 totalFramesRead += framesRead; 63497 63498 if (framesRead < (int)framesRemaining) { 63499 break; /* Nothing left to read. Get out. */ 63500 } 63501 } 63502 } 63503 } else { 63504 result = MA_INVALID_ARGS; 63505 } 63506 63507 pVorbis->cursor += totalFramesRead; 63508 63509 if (totalFramesRead == 0) { 63510 result = MA_AT_END; 63511 } 63512 63513 if (pFramesRead != NULL) { 63514 *pFramesRead = totalFramesRead; 63515 } 63516 63517 if (result == MA_SUCCESS && totalFramesRead == 0) { 63518 result = MA_AT_END; 63519 } 63520 63521 return result; 63522 } 63523 #else 63524 { 63525 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63526 MA_ASSERT(MA_FALSE); 63527 63528 (void)pFramesOut; 63529 (void)frameCount; 63530 (void)pFramesRead; 63531 63532 return MA_NOT_IMPLEMENTED; 63533 } 63534 #endif 63535 } 63536 63537 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) 63538 { 63539 if (pVorbis == NULL) { 63540 return MA_INVALID_ARGS; 63541 } 63542 63543 #if !defined(MA_NO_VORBIS) 63544 { 63545 /* Different seeking methods depending on whether or not we're using push mode. */ 63546 if (pVorbis->usingPushMode) { 63547 /* Push mode. This is the complex case. */ 63548 ma_result result; 63549 float buffer[4096]; 63550 63551 /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ 63552 if (frameIndex < pVorbis->cursor) { 63553 if (frameIndex > 0x7FFFFFFF) { 63554 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ 63555 } 63556 63557 /* 63558 This is wildly inefficient due to me having trouble getting sample exact seeking working 63559 robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work 63560 perfectly is to reinitialize the decoder. Note that we only enter this path when seeking 63561 backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. 63562 */ 63563 stb_vorbis_close(pVorbis->stb); 63564 ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); 63565 63566 MA_ZERO_OBJECT(&pVorbis->push); 63567 63568 /* Seek to the start of the file. */ 63569 result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); 63570 if (result != MA_SUCCESS) { 63571 return result; 63572 } 63573 63574 result = ma_stbvorbis_init_internal_decoder_push(pVorbis); 63575 if (result != MA_SUCCESS) { 63576 return result; 63577 } 63578 63579 /* At this point we should be sitting on the first frame. */ 63580 pVorbis->cursor = 0; 63581 } 63582 63583 /* We're just brute-forcing this for now. */ 63584 while (pVorbis->cursor < frameIndex) { 63585 ma_uint64 framesRead; 63586 ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; 63587 if (framesToRead > (frameIndex - pVorbis->cursor)) { 63588 framesToRead = (frameIndex - pVorbis->cursor); 63589 } 63590 63591 result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); 63592 if (result != MA_SUCCESS) { 63593 return result; 63594 } 63595 } 63596 } else { 63597 /* Pull mode. This is the simple case. */ 63598 int vorbisResult; 63599 63600 if (frameIndex > UINT_MAX) { 63601 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ 63602 } 63603 63604 vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ 63605 if (vorbisResult == 0) { 63606 return MA_ERROR; /* See failed. */ 63607 } 63608 63609 pVorbis->cursor = frameIndex; 63610 } 63611 63612 return MA_SUCCESS; 63613 } 63614 #else 63615 { 63616 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63617 MA_ASSERT(MA_FALSE); 63618 63619 (void)frameIndex; 63620 63621 return MA_NOT_IMPLEMENTED; 63622 } 63623 #endif 63624 } 63625 63626 MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 63627 { 63628 /* Defaults for safety. */ 63629 if (pFormat != NULL) { 63630 *pFormat = ma_format_unknown; 63631 } 63632 if (pChannels != NULL) { 63633 *pChannels = 0; 63634 } 63635 if (pSampleRate != NULL) { 63636 *pSampleRate = 0; 63637 } 63638 if (pChannelMap != NULL) { 63639 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 63640 } 63641 63642 if (pVorbis == NULL) { 63643 return MA_INVALID_OPERATION; 63644 } 63645 63646 if (pFormat != NULL) { 63647 *pFormat = pVorbis->format; 63648 } 63649 63650 #if !defined(MA_NO_VORBIS) 63651 { 63652 if (pChannels != NULL) { 63653 *pChannels = pVorbis->channels; 63654 } 63655 63656 if (pSampleRate != NULL) { 63657 *pSampleRate = pVorbis->sampleRate; 63658 } 63659 63660 if (pChannelMap != NULL) { 63661 ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); 63662 } 63663 63664 return MA_SUCCESS; 63665 } 63666 #else 63667 { 63668 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63669 MA_ASSERT(MA_FALSE); 63670 return MA_NOT_IMPLEMENTED; 63671 } 63672 #endif 63673 } 63674 63675 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) 63676 { 63677 if (pCursor == NULL) { 63678 return MA_INVALID_ARGS; 63679 } 63680 63681 *pCursor = 0; /* Safety. */ 63682 63683 if (pVorbis == NULL) { 63684 return MA_INVALID_ARGS; 63685 } 63686 63687 #if !defined(MA_NO_VORBIS) 63688 { 63689 *pCursor = pVorbis->cursor; 63690 63691 return MA_SUCCESS; 63692 } 63693 #else 63694 { 63695 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63696 MA_ASSERT(MA_FALSE); 63697 return MA_NOT_IMPLEMENTED; 63698 } 63699 #endif 63700 } 63701 63702 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) 63703 { 63704 if (pLength == NULL) { 63705 return MA_INVALID_ARGS; 63706 } 63707 63708 *pLength = 0; /* Safety. */ 63709 63710 if (pVorbis == NULL) { 63711 return MA_INVALID_ARGS; 63712 } 63713 63714 #if !defined(MA_NO_VORBIS) 63715 { 63716 if (pVorbis->usingPushMode) { 63717 *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ 63718 } else { 63719 *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); 63720 } 63721 63722 return MA_SUCCESS; 63723 } 63724 #else 63725 { 63726 /* vorbis is disabled. Should never hit this since initialization would have failed. */ 63727 MA_ASSERT(MA_FALSE); 63728 return MA_NOT_IMPLEMENTED; 63729 } 63730 #endif 63731 } 63732 63733 63734 static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 63735 { 63736 ma_result result; 63737 ma_stbvorbis* pVorbis; 63738 63739 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 63740 63741 /* For now we're just allocating the decoder backend on the heap. */ 63742 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); 63743 if (pVorbis == NULL) { 63744 return MA_OUT_OF_MEMORY; 63745 } 63746 63747 result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); 63748 if (result != MA_SUCCESS) { 63749 ma_free(pVorbis, pAllocationCallbacks); 63750 return result; 63751 } 63752 63753 *ppBackend = pVorbis; 63754 63755 return MA_SUCCESS; 63756 } 63757 63758 static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 63759 { 63760 ma_result result; 63761 ma_stbvorbis* pVorbis; 63762 63763 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 63764 63765 /* For now we're just allocating the decoder backend on the heap. */ 63766 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); 63767 if (pVorbis == NULL) { 63768 return MA_OUT_OF_MEMORY; 63769 } 63770 63771 result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); 63772 if (result != MA_SUCCESS) { 63773 ma_free(pVorbis, pAllocationCallbacks); 63774 return result; 63775 } 63776 63777 *ppBackend = pVorbis; 63778 63779 return MA_SUCCESS; 63780 } 63781 63782 static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) 63783 { 63784 ma_result result; 63785 ma_stbvorbis* pVorbis; 63786 63787 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ 63788 63789 /* For now we're just allocating the decoder backend on the heap. */ 63790 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); 63791 if (pVorbis == NULL) { 63792 return MA_OUT_OF_MEMORY; 63793 } 63794 63795 result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); 63796 if (result != MA_SUCCESS) { 63797 ma_free(pVorbis, pAllocationCallbacks); 63798 return result; 63799 } 63800 63801 *ppBackend = pVorbis; 63802 63803 return MA_SUCCESS; 63804 } 63805 63806 static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) 63807 { 63808 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; 63809 63810 (void)pUserData; 63811 63812 ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); 63813 ma_free(pVorbis, pAllocationCallbacks); 63814 } 63815 63816 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = 63817 { 63818 ma_decoding_backend_init__stbvorbis, 63819 ma_decoding_backend_init_file__stbvorbis, 63820 NULL, /* onInitFileW() */ 63821 ma_decoding_backend_init_memory__stbvorbis, 63822 ma_decoding_backend_uninit__stbvorbis 63823 }; 63824 63825 static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63826 { 63827 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); 63828 } 63829 63830 static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63831 { 63832 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); 63833 } 63834 63835 static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63836 { 63837 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); 63838 } 63839 63840 static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63841 { 63842 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); 63843 } 63844 #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ 63845 63846 63847 63848 static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63849 { 63850 MA_ASSERT(pDecoder != NULL); 63851 63852 if (pConfig != NULL) { 63853 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); 63854 } else { 63855 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); 63856 return MA_SUCCESS; 63857 } 63858 } 63859 63860 static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 63861 { 63862 return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); 63863 } 63864 63865 static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 63866 { 63867 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); 63868 } 63869 63870 static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 63871 { 63872 return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 63873 } 63874 63875 static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 63876 { 63877 return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); 63878 } 63879 63880 static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) 63881 { 63882 return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); 63883 } 63884 63885 static ma_data_source_vtable g_ma_decoder_data_source_vtable = 63886 { 63887 ma_decoder__data_source_on_read, 63888 ma_decoder__data_source_on_seek, 63889 ma_decoder__data_source_on_get_data_format, 63890 ma_decoder__data_source_on_get_cursor, 63891 ma_decoder__data_source_on_get_length, 63892 NULL, /* onSetLooping */ 63893 0 63894 }; 63895 63896 static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63897 { 63898 ma_result result; 63899 ma_data_source_config dataSourceConfig; 63900 63901 MA_ASSERT(pConfig != NULL); 63902 63903 if (pDecoder == NULL) { 63904 return MA_INVALID_ARGS; 63905 } 63906 63907 MA_ZERO_OBJECT(pDecoder); 63908 63909 dataSourceConfig = ma_data_source_config_init(); 63910 dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; 63911 63912 result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); 63913 if (result != MA_SUCCESS) { 63914 return result; 63915 } 63916 63917 pDecoder->onRead = onRead; 63918 pDecoder->onSeek = onSeek; 63919 pDecoder->onTell = onTell; 63920 pDecoder->pUserData = pUserData; 63921 63922 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); 63923 if (result != MA_SUCCESS) { 63924 ma_data_source_uninit(&pDecoder->ds); 63925 return result; 63926 } 63927 63928 return MA_SUCCESS; 63929 } 63930 63931 static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63932 { 63933 ma_result result; 63934 63935 result = ma_decoder__init_data_converter(pDecoder, pConfig); 63936 63937 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ 63938 if (result != MA_SUCCESS) { 63939 ma_decoder_uninit(pDecoder); 63940 return result; 63941 } 63942 63943 return result; 63944 } 63945 63946 63947 static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 63948 { 63949 ma_result result = MA_NO_BACKEND; 63950 63951 MA_ASSERT(pConfig != NULL); 63952 MA_ASSERT(pDecoder != NULL); 63953 63954 /* Silence some warnings in the case that we don't have any decoder backends enabled. */ 63955 (void)onRead; 63956 (void)onSeek; 63957 (void)pUserData; 63958 63959 63960 /* If we've specified a specific encoding type, try that first. */ 63961 if (pConfig->encodingFormat != ma_encoding_format_unknown) { 63962 #ifdef MA_HAS_WAV 63963 if (pConfig->encodingFormat == ma_encoding_format_wav) { 63964 result = ma_decoder_init_wav__internal(pConfig, pDecoder); 63965 } 63966 #endif 63967 #ifdef MA_HAS_FLAC 63968 if (pConfig->encodingFormat == ma_encoding_format_flac) { 63969 result = ma_decoder_init_flac__internal(pConfig, pDecoder); 63970 } 63971 #endif 63972 #ifdef MA_HAS_MP3 63973 if (pConfig->encodingFormat == ma_encoding_format_mp3) { 63974 result = ma_decoder_init_mp3__internal(pConfig, pDecoder); 63975 } 63976 #endif 63977 #ifdef MA_HAS_VORBIS 63978 if (pConfig->encodingFormat == ma_encoding_format_vorbis) { 63979 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); 63980 } 63981 #endif 63982 63983 /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ 63984 if (result != MA_SUCCESS) { 63985 onSeek(pDecoder, 0, ma_seek_origin_start); 63986 } 63987 } 63988 63989 if (result != MA_SUCCESS) { 63990 /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ 63991 63992 /* 63993 We use trial and error to open a decoder. We prioritize custom decoders so that if they 63994 implement the same encoding format they take priority over the built-in decoders. 63995 */ 63996 if (result != MA_SUCCESS) { 63997 result = ma_decoder_init_custom__internal(pConfig, pDecoder); 63998 if (result != MA_SUCCESS) { 63999 onSeek(pDecoder, 0, ma_seek_origin_start); 64000 } 64001 } 64002 64003 /* 64004 If we get to this point and we still haven't found a decoder, and the caller has requested a 64005 specific encoding format, there's no hope for it. Abort. 64006 */ 64007 if (pConfig->encodingFormat != ma_encoding_format_unknown) { 64008 return MA_NO_BACKEND; 64009 } 64010 64011 #ifdef MA_HAS_WAV 64012 if (result != MA_SUCCESS) { 64013 result = ma_decoder_init_wav__internal(pConfig, pDecoder); 64014 if (result != MA_SUCCESS) { 64015 onSeek(pDecoder, 0, ma_seek_origin_start); 64016 } 64017 } 64018 #endif 64019 #ifdef MA_HAS_FLAC 64020 if (result != MA_SUCCESS) { 64021 result = ma_decoder_init_flac__internal(pConfig, pDecoder); 64022 if (result != MA_SUCCESS) { 64023 onSeek(pDecoder, 0, ma_seek_origin_start); 64024 } 64025 } 64026 #endif 64027 #ifdef MA_HAS_MP3 64028 if (result != MA_SUCCESS) { 64029 result = ma_decoder_init_mp3__internal(pConfig, pDecoder); 64030 if (result != MA_SUCCESS) { 64031 onSeek(pDecoder, 0, ma_seek_origin_start); 64032 } 64033 } 64034 #endif 64035 #ifdef MA_HAS_VORBIS 64036 if (result != MA_SUCCESS) { 64037 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); 64038 if (result != MA_SUCCESS) { 64039 onSeek(pDecoder, 0, ma_seek_origin_start); 64040 } 64041 } 64042 #endif 64043 } 64044 64045 if (result != MA_SUCCESS) { 64046 return result; 64047 } 64048 64049 return ma_decoder__postinit(pConfig, pDecoder); 64050 } 64051 64052 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64053 { 64054 ma_decoder_config config; 64055 ma_result result; 64056 64057 config = ma_decoder_config_init_copy(pConfig); 64058 64059 result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); 64060 if (result != MA_SUCCESS) { 64061 return result; 64062 } 64063 64064 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); 64065 } 64066 64067 64068 static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) 64069 { 64070 size_t bytesRemaining; 64071 64072 MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); 64073 64074 if (pBytesRead != NULL) { 64075 *pBytesRead = 0; 64076 } 64077 64078 bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; 64079 if (bytesToRead > bytesRemaining) { 64080 bytesToRead = bytesRemaining; 64081 } 64082 64083 if (bytesRemaining == 0) { 64084 return MA_AT_END; 64085 } 64086 64087 if (bytesToRead > 0) { 64088 MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); 64089 pDecoder->data.memory.currentReadPos += bytesToRead; 64090 } 64091 64092 if (pBytesRead != NULL) { 64093 *pBytesRead = bytesToRead; 64094 } 64095 64096 return MA_SUCCESS; 64097 } 64098 64099 static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) 64100 { 64101 if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { 64102 return MA_BAD_SEEK; 64103 } 64104 64105 if (origin == ma_seek_origin_current) { 64106 if (byteOffset > 0) { 64107 if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { 64108 byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ 64109 } 64110 64111 pDecoder->data.memory.currentReadPos += (size_t)byteOffset; 64112 } else { 64113 if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { 64114 byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ 64115 } 64116 64117 pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; 64118 } 64119 } else { 64120 if (origin == ma_seek_origin_end) { 64121 if (byteOffset < 0) { 64122 byteOffset = -byteOffset; 64123 } 64124 64125 if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { 64126 pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ 64127 } else { 64128 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; 64129 } 64130 } else { 64131 if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { 64132 pDecoder->data.memory.currentReadPos = (size_t)byteOffset; 64133 } else { 64134 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ 64135 } 64136 } 64137 } 64138 64139 return MA_SUCCESS; 64140 } 64141 64142 static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) 64143 { 64144 MA_ASSERT(pDecoder != NULL); 64145 MA_ASSERT(pCursor != NULL); 64146 64147 *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; 64148 64149 return MA_SUCCESS; 64150 } 64151 64152 static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64153 { 64154 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); 64155 if (result != MA_SUCCESS) { 64156 return result; 64157 } 64158 64159 if (pData == NULL || dataSize == 0) { 64160 return MA_INVALID_ARGS; 64161 } 64162 64163 pDecoder->data.memory.pData = (const ma_uint8*)pData; 64164 pDecoder->data.memory.dataSize = dataSize; 64165 pDecoder->data.memory.currentReadPos = 0; 64166 64167 (void)pConfig; 64168 return MA_SUCCESS; 64169 } 64170 64171 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64172 { 64173 ma_result result; 64174 ma_decoder_config config; 64175 64176 config = ma_decoder_config_init_copy(pConfig); 64177 64178 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); 64179 if (result != MA_SUCCESS) { 64180 return result; 64181 } 64182 64183 if (pData == NULL || dataSize == 0) { 64184 return MA_INVALID_ARGS; 64185 } 64186 64187 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ 64188 result = MA_NO_BACKEND; 64189 64190 if (config.encodingFormat != ma_encoding_format_unknown) { 64191 #ifdef MA_HAS_WAV 64192 if (config.encodingFormat == ma_encoding_format_wav) { 64193 result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); 64194 } 64195 #endif 64196 #ifdef MA_HAS_FLAC 64197 if (config.encodingFormat == ma_encoding_format_flac) { 64198 result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); 64199 } 64200 #endif 64201 #ifdef MA_HAS_MP3 64202 if (config.encodingFormat == ma_encoding_format_mp3) { 64203 result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); 64204 } 64205 #endif 64206 #ifdef MA_HAS_VORBIS 64207 if (config.encodingFormat == ma_encoding_format_vorbis) { 64208 result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); 64209 } 64210 #endif 64211 } 64212 64213 if (result != MA_SUCCESS) { 64214 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ 64215 64216 /* 64217 We use trial and error to open a decoder. We prioritize custom decoders so that if they 64218 implement the same encoding format they take priority over the built-in decoders. 64219 */ 64220 result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); 64221 64222 /* 64223 If we get to this point and we still haven't found a decoder, and the caller has requested a 64224 specific encoding format, there's no hope for it. Abort. 64225 */ 64226 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { 64227 return MA_NO_BACKEND; 64228 } 64229 64230 /* Use trial and error for stock decoders. */ 64231 if (result != MA_SUCCESS) { 64232 #ifdef MA_HAS_WAV 64233 if (result != MA_SUCCESS) { 64234 result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); 64235 } 64236 #endif 64237 #ifdef MA_HAS_FLAC 64238 if (result != MA_SUCCESS) { 64239 result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); 64240 } 64241 #endif 64242 #ifdef MA_HAS_MP3 64243 if (result != MA_SUCCESS) { 64244 result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); 64245 } 64246 #endif 64247 #ifdef MA_HAS_VORBIS 64248 if (result != MA_SUCCESS) { 64249 result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); 64250 } 64251 #endif 64252 } 64253 } 64254 64255 /* 64256 If at this point we still haven't successfully initialized the decoder it most likely means 64257 the backend doesn't have an implementation for loading from a file path. We'll try using 64258 miniaudio's built-in file IO for loading file. 64259 */ 64260 if (result == MA_SUCCESS) { 64261 /* Initialization was successful. Finish up. */ 64262 result = ma_decoder__postinit(&config, pDecoder); 64263 if (result != MA_SUCCESS) { 64264 /* 64265 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely 64266 due to an out of memory error. We're going to abort with an error here and not try to recover. 64267 */ 64268 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { 64269 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); 64270 } 64271 64272 return result; 64273 } 64274 } else { 64275 /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ 64276 result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); 64277 if (result != MA_SUCCESS) { 64278 return result; 64279 } 64280 64281 result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); 64282 if (result != MA_SUCCESS) { 64283 return result; 64284 } 64285 } 64286 64287 return MA_SUCCESS; 64288 } 64289 64290 64291 #if defined(MA_HAS_WAV) || \ 64292 defined(MA_HAS_MP3) || \ 64293 defined(MA_HAS_FLAC) || \ 64294 defined(MA_HAS_VORBIS) || \ 64295 defined(MA_HAS_OPUS) 64296 #define MA_HAS_PATH_API 64297 #endif 64298 64299 #if defined(MA_HAS_PATH_API) 64300 static const char* ma_path_file_name(const char* path) 64301 { 64302 const char* fileName; 64303 64304 if (path == NULL) { 64305 return NULL; 64306 } 64307 64308 fileName = path; 64309 64310 /* We just loop through the path until we find the last slash. */ 64311 while (path[0] != '\0') { 64312 if (path[0] == '/' || path[0] == '\\') { 64313 fileName = path; 64314 } 64315 64316 path += 1; 64317 } 64318 64319 /* At this point the file name is sitting on a slash, so just move forward. */ 64320 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { 64321 fileName += 1; 64322 } 64323 64324 return fileName; 64325 } 64326 64327 static const wchar_t* ma_path_file_name_w(const wchar_t* path) 64328 { 64329 const wchar_t* fileName; 64330 64331 if (path == NULL) { 64332 return NULL; 64333 } 64334 64335 fileName = path; 64336 64337 /* We just loop through the path until we find the last slash. */ 64338 while (path[0] != '\0') { 64339 if (path[0] == '/' || path[0] == '\\') { 64340 fileName = path; 64341 } 64342 64343 path += 1; 64344 } 64345 64346 /* At this point the file name is sitting on a slash, so just move forward. */ 64347 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { 64348 fileName += 1; 64349 } 64350 64351 return fileName; 64352 } 64353 64354 64355 static const char* ma_path_extension(const char* path) 64356 { 64357 const char* extension; 64358 const char* lastOccurance; 64359 64360 if (path == NULL) { 64361 path = ""; 64362 } 64363 64364 extension = ma_path_file_name(path); 64365 lastOccurance = NULL; 64366 64367 /* Just find the last '.' and return. */ 64368 while (extension[0] != '\0') { 64369 if (extension[0] == '.') { 64370 extension += 1; 64371 lastOccurance = extension; 64372 } 64373 64374 extension += 1; 64375 } 64376 64377 return (lastOccurance != NULL) ? lastOccurance : extension; 64378 } 64379 64380 static const wchar_t* ma_path_extension_w(const wchar_t* path) 64381 { 64382 const wchar_t* extension; 64383 const wchar_t* lastOccurance; 64384 64385 if (path == NULL) { 64386 path = L""; 64387 } 64388 64389 extension = ma_path_file_name_w(path); 64390 lastOccurance = NULL; 64391 64392 /* Just find the last '.' and return. */ 64393 while (extension[0] != '\0') { 64394 if (extension[0] == '.') { 64395 extension += 1; 64396 lastOccurance = extension; 64397 } 64398 64399 extension += 1; 64400 } 64401 64402 return (lastOccurance != NULL) ? lastOccurance : extension; 64403 } 64404 64405 64406 static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) 64407 { 64408 const char* ext1; 64409 const char* ext2; 64410 64411 if (path == NULL || extension == NULL) { 64412 return MA_FALSE; 64413 } 64414 64415 ext1 = extension; 64416 ext2 = ma_path_extension(path); 64417 64418 #if defined(_MSC_VER) || defined(__DMC__) 64419 return _stricmp(ext1, ext2) == 0; 64420 #else 64421 return strcasecmp(ext1, ext2) == 0; 64422 #endif 64423 } 64424 64425 static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) 64426 { 64427 const wchar_t* ext1; 64428 const wchar_t* ext2; 64429 64430 if (path == NULL || extension == NULL) { 64431 return MA_FALSE; 64432 } 64433 64434 ext1 = extension; 64435 ext2 = ma_path_extension_w(path); 64436 64437 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) 64438 return _wcsicmp(ext1, ext2) == 0; 64439 #else 64440 /* 64441 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This 64442 isn't the most efficient way to do it, but it should work OK. 64443 */ 64444 { 64445 char ext1MB[4096]; 64446 char ext2MB[4096]; 64447 const wchar_t* pext1 = ext1; 64448 const wchar_t* pext2 = ext2; 64449 mbstate_t mbs1; 64450 mbstate_t mbs2; 64451 64452 MA_ZERO_OBJECT(&mbs1); 64453 MA_ZERO_OBJECT(&mbs2); 64454 64455 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { 64456 return MA_FALSE; 64457 } 64458 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { 64459 return MA_FALSE; 64460 } 64461 64462 return strcasecmp(ext1MB, ext2MB) == 0; 64463 } 64464 #endif 64465 } 64466 #endif /* MA_HAS_PATH_API */ 64467 64468 64469 64470 static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) 64471 { 64472 MA_ASSERT(pDecoder != NULL); 64473 MA_ASSERT(pBufferOut != NULL); 64474 64475 return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); 64476 } 64477 64478 static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) 64479 { 64480 MA_ASSERT(pDecoder != NULL); 64481 64482 return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); 64483 } 64484 64485 static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) 64486 { 64487 MA_ASSERT(pDecoder != NULL); 64488 64489 return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); 64490 } 64491 64492 static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64493 { 64494 ma_result result; 64495 ma_vfs_file file; 64496 64497 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); 64498 if (result != MA_SUCCESS) { 64499 return result; 64500 } 64501 64502 if (pFilePath == NULL || pFilePath[0] == '\0') { 64503 return MA_INVALID_ARGS; 64504 } 64505 64506 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); 64507 if (result != MA_SUCCESS) { 64508 return result; 64509 } 64510 64511 pDecoder->data.vfs.pVFS = pVFS; 64512 pDecoder->data.vfs.file = file; 64513 64514 return MA_SUCCESS; 64515 } 64516 64517 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64518 { 64519 ma_result result; 64520 ma_decoder_config config; 64521 64522 config = ma_decoder_config_init_copy(pConfig); 64523 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); 64524 if (result != MA_SUCCESS) { 64525 return result; 64526 } 64527 64528 result = MA_NO_BACKEND; 64529 64530 if (config.encodingFormat != ma_encoding_format_unknown) { 64531 #ifdef MA_HAS_WAV 64532 if (config.encodingFormat == ma_encoding_format_wav) { 64533 result = ma_decoder_init_wav__internal(&config, pDecoder); 64534 } 64535 #endif 64536 #ifdef MA_HAS_FLAC 64537 if (config.encodingFormat == ma_encoding_format_flac) { 64538 result = ma_decoder_init_flac__internal(&config, pDecoder); 64539 } 64540 #endif 64541 #ifdef MA_HAS_MP3 64542 if (config.encodingFormat == ma_encoding_format_mp3) { 64543 result = ma_decoder_init_mp3__internal(&config, pDecoder); 64544 } 64545 #endif 64546 #ifdef MA_HAS_VORBIS 64547 if (config.encodingFormat == ma_encoding_format_vorbis) { 64548 result = ma_decoder_init_vorbis__internal(&config, pDecoder); 64549 } 64550 #endif 64551 64552 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ 64553 if (result != MA_SUCCESS) { 64554 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64555 } 64556 } 64557 64558 if (result != MA_SUCCESS) { 64559 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ 64560 64561 /* 64562 We use trial and error to open a decoder. We prioritize custom decoders so that if they 64563 implement the same encoding format they take priority over the built-in decoders. 64564 */ 64565 if (result != MA_SUCCESS) { 64566 result = ma_decoder_init_custom__internal(&config, pDecoder); 64567 if (result != MA_SUCCESS) { 64568 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64569 } 64570 } 64571 64572 /* 64573 If we get to this point and we still haven't found a decoder, and the caller has requested a 64574 specific encoding format, there's no hope for it. Abort. 64575 */ 64576 if (config.encodingFormat != ma_encoding_format_unknown) { 64577 return MA_NO_BACKEND; 64578 } 64579 64580 #ifdef MA_HAS_WAV 64581 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { 64582 result = ma_decoder_init_wav__internal(&config, pDecoder); 64583 if (result != MA_SUCCESS) { 64584 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64585 } 64586 } 64587 #endif 64588 #ifdef MA_HAS_FLAC 64589 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { 64590 result = ma_decoder_init_flac__internal(&config, pDecoder); 64591 if (result != MA_SUCCESS) { 64592 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64593 } 64594 } 64595 #endif 64596 #ifdef MA_HAS_MP3 64597 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { 64598 result = ma_decoder_init_mp3__internal(&config, pDecoder); 64599 if (result != MA_SUCCESS) { 64600 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64601 } 64602 } 64603 #endif 64604 } 64605 64606 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ 64607 if (result != MA_SUCCESS) { 64608 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); 64609 } else { 64610 result = ma_decoder__postinit(&config, pDecoder); 64611 } 64612 64613 if (result != MA_SUCCESS) { 64614 if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ 64615 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); 64616 } 64617 64618 return result; 64619 } 64620 64621 return MA_SUCCESS; 64622 } 64623 64624 64625 static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64626 { 64627 ma_result result; 64628 ma_vfs_file file; 64629 64630 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); 64631 if (result != MA_SUCCESS) { 64632 return result; 64633 } 64634 64635 if (pFilePath == NULL || pFilePath[0] == '\0') { 64636 return MA_INVALID_ARGS; 64637 } 64638 64639 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); 64640 if (result != MA_SUCCESS) { 64641 return result; 64642 } 64643 64644 pDecoder->data.vfs.pVFS = pVFS; 64645 pDecoder->data.vfs.file = file; 64646 64647 return MA_SUCCESS; 64648 } 64649 64650 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64651 { 64652 ma_result result; 64653 ma_decoder_config config; 64654 64655 config = ma_decoder_config_init_copy(pConfig); 64656 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); 64657 if (result != MA_SUCCESS) { 64658 return result; 64659 } 64660 64661 result = MA_NO_BACKEND; 64662 64663 if (config.encodingFormat != ma_encoding_format_unknown) { 64664 #ifdef MA_HAS_WAV 64665 if (config.encodingFormat == ma_encoding_format_wav) { 64666 result = ma_decoder_init_wav__internal(&config, pDecoder); 64667 } 64668 #endif 64669 #ifdef MA_HAS_FLAC 64670 if (config.encodingFormat == ma_encoding_format_flac) { 64671 result = ma_decoder_init_flac__internal(&config, pDecoder); 64672 } 64673 #endif 64674 #ifdef MA_HAS_MP3 64675 if (config.encodingFormat == ma_encoding_format_mp3) { 64676 result = ma_decoder_init_mp3__internal(&config, pDecoder); 64677 } 64678 #endif 64679 #ifdef MA_HAS_VORBIS 64680 if (config.encodingFormat == ma_encoding_format_vorbis) { 64681 result = ma_decoder_init_vorbis__internal(&config, pDecoder); 64682 } 64683 #endif 64684 64685 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ 64686 if (result != MA_SUCCESS) { 64687 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64688 } 64689 } 64690 64691 if (result != MA_SUCCESS) { 64692 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ 64693 64694 /* 64695 We use trial and error to open a decoder. We prioritize custom decoders so that if they 64696 implement the same encoding format they take priority over the built-in decoders. 64697 */ 64698 if (result != MA_SUCCESS) { 64699 result = ma_decoder_init_custom__internal(&config, pDecoder); 64700 if (result != MA_SUCCESS) { 64701 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64702 } 64703 } 64704 64705 /* 64706 If we get to this point and we still haven't found a decoder, and the caller has requested a 64707 specific encoding format, there's no hope for it. Abort. 64708 */ 64709 if (config.encodingFormat != ma_encoding_format_unknown) { 64710 return MA_NO_BACKEND; 64711 } 64712 64713 #ifdef MA_HAS_WAV 64714 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { 64715 result = ma_decoder_init_wav__internal(&config, pDecoder); 64716 if (result != MA_SUCCESS) { 64717 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64718 } 64719 } 64720 #endif 64721 #ifdef MA_HAS_FLAC 64722 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { 64723 result = ma_decoder_init_flac__internal(&config, pDecoder); 64724 if (result != MA_SUCCESS) { 64725 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64726 } 64727 } 64728 #endif 64729 #ifdef MA_HAS_MP3 64730 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { 64731 result = ma_decoder_init_mp3__internal(&config, pDecoder); 64732 if (result != MA_SUCCESS) { 64733 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); 64734 } 64735 } 64736 #endif 64737 } 64738 64739 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ 64740 if (result != MA_SUCCESS) { 64741 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); 64742 } else { 64743 result = ma_decoder__postinit(&config, pDecoder); 64744 } 64745 64746 if (result != MA_SUCCESS) { 64747 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); 64748 return result; 64749 } 64750 64751 return MA_SUCCESS; 64752 } 64753 64754 64755 static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64756 { 64757 ma_result result; 64758 64759 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); 64760 if (result != MA_SUCCESS) { 64761 return result; 64762 } 64763 64764 if (pFilePath == NULL || pFilePath[0] == '\0') { 64765 return MA_INVALID_ARGS; 64766 } 64767 64768 return MA_SUCCESS; 64769 } 64770 64771 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64772 { 64773 ma_result result; 64774 ma_decoder_config config; 64775 64776 config = ma_decoder_config_init_copy(pConfig); 64777 result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); 64778 if (result != MA_SUCCESS) { 64779 return result; 64780 } 64781 64782 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ 64783 result = MA_NO_BACKEND; 64784 64785 if (config.encodingFormat != ma_encoding_format_unknown) { 64786 #ifdef MA_HAS_WAV 64787 if (config.encodingFormat == ma_encoding_format_wav) { 64788 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); 64789 } 64790 #endif 64791 #ifdef MA_HAS_FLAC 64792 if (config.encodingFormat == ma_encoding_format_flac) { 64793 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); 64794 } 64795 #endif 64796 #ifdef MA_HAS_MP3 64797 if (config.encodingFormat == ma_encoding_format_mp3) { 64798 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); 64799 } 64800 #endif 64801 #ifdef MA_HAS_VORBIS 64802 if (config.encodingFormat == ma_encoding_format_vorbis) { 64803 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); 64804 } 64805 #endif 64806 } 64807 64808 if (result != MA_SUCCESS) { 64809 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ 64810 64811 /* 64812 We use trial and error to open a decoder. We prioritize custom decoders so that if they 64813 implement the same encoding format they take priority over the built-in decoders. 64814 */ 64815 result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); 64816 64817 /* 64818 If we get to this point and we still haven't found a decoder, and the caller has requested a 64819 specific encoding format, there's no hope for it. Abort. 64820 */ 64821 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { 64822 return MA_NO_BACKEND; 64823 } 64824 64825 /* First try loading based on the file extension so we don't waste time opening and closing files. */ 64826 #ifdef MA_HAS_WAV 64827 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { 64828 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); 64829 } 64830 #endif 64831 #ifdef MA_HAS_FLAC 64832 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { 64833 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); 64834 } 64835 #endif 64836 #ifdef MA_HAS_MP3 64837 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { 64838 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); 64839 } 64840 #endif 64841 #ifdef MA_HAS_VORBIS 64842 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { 64843 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); 64844 } 64845 #endif 64846 64847 /* 64848 If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we 64849 need only iterate over our stock decoders. 64850 */ 64851 if (result != MA_SUCCESS) { 64852 #ifdef MA_HAS_WAV 64853 if (result != MA_SUCCESS) { 64854 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); 64855 } 64856 #endif 64857 #ifdef MA_HAS_FLAC 64858 if (result != MA_SUCCESS) { 64859 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); 64860 } 64861 #endif 64862 #ifdef MA_HAS_MP3 64863 if (result != MA_SUCCESS) { 64864 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); 64865 } 64866 #endif 64867 #ifdef MA_HAS_VORBIS 64868 if (result != MA_SUCCESS) { 64869 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); 64870 } 64871 #endif 64872 } 64873 } 64874 64875 /* 64876 If at this point we still haven't successfully initialized the decoder it most likely means 64877 the backend doesn't have an implementation for loading from a file path. We'll try using 64878 miniaudio's built-in file IO for loading file. 64879 */ 64880 if (result == MA_SUCCESS) { 64881 /* Initialization was successful. Finish up. */ 64882 result = ma_decoder__postinit(&config, pDecoder); 64883 if (result != MA_SUCCESS) { 64884 /* 64885 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely 64886 due to an out of memory error. We're going to abort with an error here and not try to recover. 64887 */ 64888 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { 64889 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); 64890 } 64891 64892 return result; 64893 } 64894 } else { 64895 /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ 64896 result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); 64897 if (result != MA_SUCCESS) { 64898 return result; 64899 } 64900 } 64901 64902 return MA_SUCCESS; 64903 } 64904 64905 static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64906 { 64907 ma_result result; 64908 64909 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); 64910 if (result != MA_SUCCESS) { 64911 return result; 64912 } 64913 64914 if (pFilePath == NULL || pFilePath[0] == '\0') { 64915 return MA_INVALID_ARGS; 64916 } 64917 64918 return MA_SUCCESS; 64919 } 64920 64921 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) 64922 { 64923 ma_result result; 64924 ma_decoder_config config; 64925 64926 config = ma_decoder_config_init_copy(pConfig); 64927 result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); 64928 if (result != MA_SUCCESS) { 64929 return result; 64930 } 64931 64932 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ 64933 result = MA_NO_BACKEND; 64934 64935 if (config.encodingFormat != ma_encoding_format_unknown) { 64936 #ifdef MA_HAS_WAV 64937 if (config.encodingFormat == ma_encoding_format_wav) { 64938 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); 64939 } 64940 #endif 64941 #ifdef MA_HAS_FLAC 64942 if (config.encodingFormat == ma_encoding_format_flac) { 64943 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); 64944 } 64945 #endif 64946 #ifdef MA_HAS_MP3 64947 if (config.encodingFormat == ma_encoding_format_mp3) { 64948 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); 64949 } 64950 #endif 64951 #ifdef MA_HAS_VORBIS 64952 if (config.encodingFormat == ma_encoding_format_vorbis) { 64953 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); 64954 } 64955 #endif 64956 } 64957 64958 if (result != MA_SUCCESS) { 64959 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ 64960 64961 /* 64962 We use trial and error to open a decoder. We prioritize custom decoders so that if they 64963 implement the same encoding format they take priority over the built-in decoders. 64964 */ 64965 result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); 64966 64967 /* 64968 If we get to this point and we still haven't found a decoder, and the caller has requested a 64969 specific encoding format, there's no hope for it. Abort. 64970 */ 64971 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { 64972 return MA_NO_BACKEND; 64973 } 64974 64975 /* First try loading based on the file extension so we don't waste time opening and closing files. */ 64976 #ifdef MA_HAS_WAV 64977 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { 64978 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); 64979 } 64980 #endif 64981 #ifdef MA_HAS_FLAC 64982 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { 64983 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); 64984 } 64985 #endif 64986 #ifdef MA_HAS_MP3 64987 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { 64988 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); 64989 } 64990 #endif 64991 #ifdef MA_HAS_VORBIS 64992 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { 64993 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); 64994 } 64995 #endif 64996 64997 /* 64998 If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we 64999 need only iterate over our stock decoders. 65000 */ 65001 if (result != MA_SUCCESS) { 65002 #ifdef MA_HAS_WAV 65003 if (result != MA_SUCCESS) { 65004 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); 65005 } 65006 #endif 65007 #ifdef MA_HAS_FLAC 65008 if (result != MA_SUCCESS) { 65009 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); 65010 } 65011 #endif 65012 #ifdef MA_HAS_MP3 65013 if (result != MA_SUCCESS) { 65014 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); 65015 } 65016 #endif 65017 #ifdef MA_HAS_VORBIS 65018 if (result != MA_SUCCESS) { 65019 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); 65020 } 65021 #endif 65022 } 65023 } 65024 65025 /* 65026 If at this point we still haven't successfully initialized the decoder it most likely means 65027 the backend doesn't have an implementation for loading from a file path. We'll try using 65028 miniaudio's built-in file IO for loading file. 65029 */ 65030 if (result == MA_SUCCESS) { 65031 /* Initialization was successful. Finish up. */ 65032 result = ma_decoder__postinit(&config, pDecoder); 65033 if (result != MA_SUCCESS) { 65034 /* 65035 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely 65036 due to an out of memory error. We're going to abort with an error here and not try to recover. 65037 */ 65038 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { 65039 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); 65040 } 65041 65042 return result; 65043 } 65044 } else { 65045 /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ 65046 result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); 65047 if (result != MA_SUCCESS) { 65048 return result; 65049 } 65050 } 65051 65052 return MA_SUCCESS; 65053 } 65054 65055 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) 65056 { 65057 if (pDecoder == NULL) { 65058 return MA_INVALID_ARGS; 65059 } 65060 65061 if (pDecoder->pBackend != NULL) { 65062 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { 65063 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); 65064 } 65065 } 65066 65067 if (pDecoder->onRead == ma_decoder__on_read_vfs) { 65068 ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); 65069 pDecoder->data.vfs.file = NULL; 65070 } 65071 65072 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); 65073 ma_data_source_uninit(&pDecoder->ds); 65074 65075 if (pDecoder->pInputCache != NULL) { 65076 ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); 65077 } 65078 65079 return MA_SUCCESS; 65080 } 65081 65082 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 65083 { 65084 ma_result result = MA_SUCCESS; 65085 ma_uint64 totalFramesReadOut; 65086 void* pRunningFramesOut; 65087 65088 if (pFramesRead != NULL) { 65089 *pFramesRead = 0; /* Safety. */ 65090 } 65091 65092 if (frameCount == 0) { 65093 return MA_INVALID_ARGS; 65094 } 65095 65096 if (pDecoder == NULL) { 65097 return MA_INVALID_ARGS; 65098 } 65099 65100 if (pDecoder->pBackend == NULL) { 65101 return MA_INVALID_OPERATION; 65102 } 65103 65104 /* Fast path. */ 65105 if (pDecoder->converter.isPassthrough) { 65106 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); 65107 } else { 65108 /* 65109 Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we 65110 need to run through each sample because we need to ensure it's internal cache is updated. 65111 */ 65112 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { 65113 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); 65114 } else { 65115 /* Slow path. Need to run everything through the data converter. */ 65116 ma_format internalFormat; 65117 ma_uint32 internalChannels; 65118 65119 totalFramesReadOut = 0; 65120 pRunningFramesOut = pFramesOut; 65121 65122 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); 65123 if (result != MA_SUCCESS) { 65124 return result; /* Failed to retrieve the internal format and channel count. */ 65125 } 65126 65127 /* 65128 We run a different path depending on whether or not we are using a heap-allocated 65129 intermediary buffer or not. If the data converter does not support the calculation of 65130 the required number of input frames, we'll use the heap-allocated path. Otherwise we'll 65131 use the stack-allocated path. 65132 */ 65133 if (pDecoder->pInputCache != NULL) { 65134 /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ 65135 while (totalFramesReadOut < frameCount) { 65136 ma_uint64 framesToReadThisIterationIn; 65137 ma_uint64 framesToReadThisIterationOut; 65138 65139 /* If there's any data available in the cache, that needs to get processed first. */ 65140 if (pDecoder->inputCacheRemaining > 0) { 65141 framesToReadThisIterationOut = (frameCount - totalFramesReadOut); 65142 framesToReadThisIterationIn = framesToReadThisIterationOut; 65143 if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { 65144 framesToReadThisIterationIn = pDecoder->inputCacheRemaining; 65145 } 65146 65147 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); 65148 if (result != MA_SUCCESS) { 65149 break; 65150 } 65151 65152 pDecoder->inputCacheConsumed += framesToReadThisIterationIn; 65153 pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; 65154 65155 totalFramesReadOut += framesToReadThisIterationOut; 65156 65157 if (pRunningFramesOut != NULL) { 65158 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); 65159 } 65160 65161 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { 65162 break; /* We're done. */ 65163 } 65164 } 65165 65166 /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ 65167 if (pDecoder->inputCacheRemaining == 0) { 65168 pDecoder->inputCacheConsumed = 0; 65169 65170 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); 65171 if (result != MA_SUCCESS) { 65172 break; 65173 } 65174 } 65175 } 65176 } else { 65177 /* We have a way of determining the required number of input frames so just use the stack. */ 65178 while (totalFramesReadOut < frameCount) { 65179 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ 65180 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); 65181 ma_uint64 framesToReadThisIterationIn; 65182 ma_uint64 framesReadThisIterationIn; 65183 ma_uint64 framesToReadThisIterationOut; 65184 ma_uint64 framesReadThisIterationOut; 65185 ma_uint64 requiredInputFrameCount; 65186 65187 framesToReadThisIterationOut = (frameCount - totalFramesReadOut); 65188 framesToReadThisIterationIn = framesToReadThisIterationOut; 65189 if (framesToReadThisIterationIn > intermediaryBufferCap) { 65190 framesToReadThisIterationIn = intermediaryBufferCap; 65191 } 65192 65193 ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); 65194 if (framesToReadThisIterationIn > requiredInputFrameCount) { 65195 framesToReadThisIterationIn = requiredInputFrameCount; 65196 } 65197 65198 if (requiredInputFrameCount > 0) { 65199 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); 65200 } else { 65201 framesReadThisIterationIn = 0; 65202 } 65203 65204 /* 65205 At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any 65206 input frames, we still want to try processing frames because there may some output frames generated from cached input data. 65207 */ 65208 framesReadThisIterationOut = framesToReadThisIterationOut; 65209 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); 65210 if (result != MA_SUCCESS) { 65211 break; 65212 } 65213 65214 totalFramesReadOut += framesReadThisIterationOut; 65215 65216 if (pRunningFramesOut != NULL) { 65217 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); 65218 } 65219 65220 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { 65221 break; /* We're done. */ 65222 } 65223 } 65224 } 65225 } 65226 } 65227 65228 pDecoder->readPointerInPCMFrames += totalFramesReadOut; 65229 65230 if (pFramesRead != NULL) { 65231 *pFramesRead = totalFramesReadOut; 65232 } 65233 65234 if (result == MA_SUCCESS && totalFramesReadOut == 0) { 65235 result = MA_AT_END; 65236 } 65237 65238 return result; 65239 } 65240 65241 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) 65242 { 65243 if (pDecoder == NULL) { 65244 return MA_INVALID_ARGS; 65245 } 65246 65247 if (pDecoder->pBackend != NULL) { 65248 ma_result result; 65249 ma_uint64 internalFrameIndex; 65250 ma_uint32 internalSampleRate; 65251 ma_uint64 currentFrameIndex; 65252 65253 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); 65254 if (result != MA_SUCCESS) { 65255 return result; /* Failed to retrieve the internal sample rate. */ 65256 } 65257 65258 if (internalSampleRate == pDecoder->outputSampleRate) { 65259 internalFrameIndex = frameIndex; 65260 } else { 65261 internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); 65262 } 65263 65264 /* Only seek if we're requesting a different frame to what we're currently sitting on. */ 65265 ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); 65266 if (currentFrameIndex != internalFrameIndex) { 65267 result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); 65268 if (result == MA_SUCCESS) { 65269 pDecoder->readPointerInPCMFrames = frameIndex; 65270 } 65271 65272 /* Reset the data converter so that any cached data in the resampler is cleared. */ 65273 ma_data_converter_reset(&pDecoder->converter); 65274 } 65275 65276 return result; 65277 } 65278 65279 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ 65280 return MA_INVALID_ARGS; 65281 } 65282 65283 MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 65284 { 65285 if (pDecoder == NULL) { 65286 return MA_INVALID_ARGS; 65287 } 65288 65289 if (pFormat != NULL) { 65290 *pFormat = pDecoder->outputFormat; 65291 } 65292 65293 if (pChannels != NULL) { 65294 *pChannels = pDecoder->outputChannels; 65295 } 65296 65297 if (pSampleRate != NULL) { 65298 *pSampleRate = pDecoder->outputSampleRate; 65299 } 65300 65301 if (pChannelMap != NULL) { 65302 ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); 65303 } 65304 65305 return MA_SUCCESS; 65306 } 65307 65308 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) 65309 { 65310 if (pCursor == NULL) { 65311 return MA_INVALID_ARGS; 65312 } 65313 65314 *pCursor = 0; 65315 65316 if (pDecoder == NULL) { 65317 return MA_INVALID_ARGS; 65318 } 65319 65320 *pCursor = pDecoder->readPointerInPCMFrames; 65321 65322 return MA_SUCCESS; 65323 } 65324 65325 MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) 65326 { 65327 if (pLength == NULL) { 65328 return MA_INVALID_ARGS; 65329 } 65330 65331 *pLength = 0; 65332 65333 if (pDecoder == NULL) { 65334 return MA_INVALID_ARGS; 65335 } 65336 65337 if (pDecoder->pBackend != NULL) { 65338 ma_result result; 65339 ma_uint64 internalLengthInPCMFrames; 65340 ma_uint32 internalSampleRate; 65341 65342 result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); 65343 if (result != MA_SUCCESS) { 65344 return result; /* Failed to retrieve the internal length. */ 65345 } 65346 65347 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); 65348 if (result != MA_SUCCESS) { 65349 return result; /* Failed to retrieve the internal sample rate. */ 65350 } 65351 65352 if (internalSampleRate == pDecoder->outputSampleRate) { 65353 *pLength = internalLengthInPCMFrames; 65354 } else { 65355 *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); 65356 } 65357 65358 return MA_SUCCESS; 65359 } else { 65360 return MA_NO_BACKEND; 65361 } 65362 } 65363 65364 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) 65365 { 65366 ma_result result; 65367 ma_uint64 totalFrameCount; 65368 65369 if (pAvailableFrames == NULL) { 65370 return MA_INVALID_ARGS; 65371 } 65372 65373 *pAvailableFrames = 0; 65374 65375 if (pDecoder == NULL) { 65376 return MA_INVALID_ARGS; 65377 } 65378 65379 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); 65380 if (result != MA_SUCCESS) { 65381 return result; 65382 } 65383 65384 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { 65385 *pAvailableFrames = 0; 65386 } else { 65387 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; 65388 } 65389 65390 return MA_SUCCESS; 65391 } 65392 65393 65394 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) 65395 { 65396 ma_result result; 65397 ma_uint64 totalFrameCount; 65398 ma_uint64 bpf; 65399 ma_uint64 dataCapInFrames; 65400 void* pPCMFramesOut; 65401 65402 MA_ASSERT(pDecoder != NULL); 65403 65404 totalFrameCount = 0; 65405 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); 65406 65407 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ 65408 dataCapInFrames = 0; 65409 pPCMFramesOut = NULL; 65410 for (;;) { 65411 ma_uint64 frameCountToTryReading; 65412 ma_uint64 framesJustRead; 65413 65414 /* Make room if there's not enough. */ 65415 if (totalFrameCount == dataCapInFrames) { 65416 void* pNewPCMFramesOut; 65417 ma_uint64 newDataCapInFrames = dataCapInFrames*2; 65418 if (newDataCapInFrames == 0) { 65419 newDataCapInFrames = 4096; 65420 } 65421 65422 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { 65423 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); 65424 return MA_TOO_BIG; 65425 } 65426 65427 pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); 65428 if (pNewPCMFramesOut == NULL) { 65429 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); 65430 return MA_OUT_OF_MEMORY; 65431 } 65432 65433 dataCapInFrames = newDataCapInFrames; 65434 pPCMFramesOut = pNewPCMFramesOut; 65435 } 65436 65437 frameCountToTryReading = dataCapInFrames - totalFrameCount; 65438 MA_ASSERT(frameCountToTryReading > 0); 65439 65440 result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); 65441 totalFrameCount += framesJustRead; 65442 65443 if (result != MA_SUCCESS) { 65444 break; 65445 } 65446 65447 if (framesJustRead < frameCountToTryReading) { 65448 break; 65449 } 65450 } 65451 65452 65453 if (pConfigOut != NULL) { 65454 pConfigOut->format = pDecoder->outputFormat; 65455 pConfigOut->channels = pDecoder->outputChannels; 65456 pConfigOut->sampleRate = pDecoder->outputSampleRate; 65457 } 65458 65459 if (ppPCMFramesOut != NULL) { 65460 *ppPCMFramesOut = pPCMFramesOut; 65461 } else { 65462 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); 65463 } 65464 65465 if (pFrameCountOut != NULL) { 65466 *pFrameCountOut = totalFrameCount; 65467 } 65468 65469 ma_decoder_uninit(pDecoder); 65470 return MA_SUCCESS; 65471 } 65472 65473 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) 65474 { 65475 ma_result result; 65476 ma_decoder_config config; 65477 ma_decoder decoder; 65478 65479 if (pFrameCountOut != NULL) { 65480 *pFrameCountOut = 0; 65481 } 65482 if (ppPCMFramesOut != NULL) { 65483 *ppPCMFramesOut = NULL; 65484 } 65485 65486 config = ma_decoder_config_init_copy(pConfig); 65487 65488 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); 65489 if (result != MA_SUCCESS) { 65490 return result; 65491 } 65492 65493 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); 65494 65495 return result; 65496 } 65497 65498 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) 65499 { 65500 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); 65501 } 65502 65503 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) 65504 { 65505 ma_decoder_config config; 65506 ma_decoder decoder; 65507 ma_result result; 65508 65509 if (pFrameCountOut != NULL) { 65510 *pFrameCountOut = 0; 65511 } 65512 if (ppPCMFramesOut != NULL) { 65513 *ppPCMFramesOut = NULL; 65514 } 65515 65516 if (pData == NULL || dataSize == 0) { 65517 return MA_INVALID_ARGS; 65518 } 65519 65520 config = ma_decoder_config_init_copy(pConfig); 65521 65522 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); 65523 if (result != MA_SUCCESS) { 65524 return result; 65525 } 65526 65527 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); 65528 } 65529 #endif /* MA_NO_DECODING */ 65530 65531 65532 #ifndef MA_NO_ENCODING 65533 65534 #if defined(MA_HAS_WAV) 65535 static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) 65536 { 65537 ma_encoder* pEncoder = (ma_encoder*)pUserData; 65538 size_t bytesWritten = 0; 65539 65540 MA_ASSERT(pEncoder != NULL); 65541 65542 pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); 65543 return bytesWritten; 65544 } 65545 65546 static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) 65547 { 65548 ma_encoder* pEncoder = (ma_encoder*)pUserData; 65549 ma_result result; 65550 65551 MA_ASSERT(pEncoder != NULL); 65552 65553 result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); 65554 if (result != MA_SUCCESS) { 65555 return MA_FALSE; 65556 } else { 65557 return MA_TRUE; 65558 } 65559 } 65560 65561 static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) 65562 { 65563 ma_dr_wav_data_format wavFormat; 65564 ma_allocation_callbacks allocationCallbacks; 65565 ma_dr_wav* pWav; 65566 65567 MA_ASSERT(pEncoder != NULL); 65568 65569 pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); 65570 if (pWav == NULL) { 65571 return MA_OUT_OF_MEMORY; 65572 } 65573 65574 wavFormat.container = ma_dr_wav_container_riff; 65575 wavFormat.channels = pEncoder->config.channels; 65576 wavFormat.sampleRate = pEncoder->config.sampleRate; 65577 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; 65578 if (pEncoder->config.format == ma_format_f32) { 65579 wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; 65580 } else { 65581 wavFormat.format = MA_DR_WAVE_FORMAT_PCM; 65582 } 65583 65584 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; 65585 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; 65586 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; 65587 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; 65588 65589 if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { 65590 return MA_ERROR; 65591 } 65592 65593 pEncoder->pInternalEncoder = pWav; 65594 65595 return MA_SUCCESS; 65596 } 65597 65598 static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) 65599 { 65600 ma_dr_wav* pWav; 65601 65602 MA_ASSERT(pEncoder != NULL); 65603 65604 pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; 65605 MA_ASSERT(pWav != NULL); 65606 65607 ma_dr_wav_uninit(pWav); 65608 ma_free(pWav, &pEncoder->config.allocationCallbacks); 65609 } 65610 65611 static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) 65612 { 65613 ma_dr_wav* pWav; 65614 ma_uint64 framesWritten; 65615 65616 MA_ASSERT(pEncoder != NULL); 65617 65618 pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; 65619 MA_ASSERT(pWav != NULL); 65620 65621 framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); 65622 65623 if (pFramesWritten != NULL) { 65624 *pFramesWritten = framesWritten; 65625 } 65626 65627 return MA_SUCCESS; 65628 } 65629 #endif 65630 65631 MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 65632 { 65633 ma_encoder_config config; 65634 65635 MA_ZERO_OBJECT(&config); 65636 config.encodingFormat = encodingFormat; 65637 config.format = format; 65638 config.channels = channels; 65639 config.sampleRate = sampleRate; 65640 65641 return config; 65642 } 65643 65644 MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65645 { 65646 ma_result result; 65647 65648 if (pEncoder == NULL) { 65649 return MA_INVALID_ARGS; 65650 } 65651 65652 MA_ZERO_OBJECT(pEncoder); 65653 65654 if (pConfig == NULL) { 65655 return MA_INVALID_ARGS; 65656 } 65657 65658 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { 65659 return MA_INVALID_ARGS; 65660 } 65661 65662 pEncoder->config = *pConfig; 65663 65664 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); 65665 if (result != MA_SUCCESS) { 65666 return result; 65667 } 65668 65669 return MA_SUCCESS; 65670 } 65671 65672 MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) 65673 { 65674 ma_result result = MA_SUCCESS; 65675 65676 /* This assumes ma_encoder_preinit() has been called prior. */ 65677 MA_ASSERT(pEncoder != NULL); 65678 65679 if (onWrite == NULL || onSeek == NULL) { 65680 return MA_INVALID_ARGS; 65681 } 65682 65683 pEncoder->onWrite = onWrite; 65684 pEncoder->onSeek = onSeek; 65685 pEncoder->pUserData = pUserData; 65686 65687 switch (pEncoder->config.encodingFormat) 65688 { 65689 case ma_encoding_format_wav: 65690 { 65691 #if defined(MA_HAS_WAV) 65692 pEncoder->onInit = ma_encoder__on_init_wav; 65693 pEncoder->onUninit = ma_encoder__on_uninit_wav; 65694 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; 65695 #else 65696 result = MA_NO_BACKEND; 65697 #endif 65698 } break; 65699 65700 default: 65701 { 65702 result = MA_INVALID_ARGS; 65703 } break; 65704 } 65705 65706 /* Getting here means we should have our backend callbacks set up. */ 65707 if (result == MA_SUCCESS) { 65708 result = pEncoder->onInit(pEncoder); 65709 } 65710 65711 return result; 65712 } 65713 65714 static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) 65715 { 65716 return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); 65717 } 65718 65719 static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) 65720 { 65721 return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); 65722 } 65723 65724 MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65725 { 65726 ma_result result; 65727 ma_vfs_file file; 65728 65729 result = ma_encoder_preinit(pConfig, pEncoder); 65730 if (result != MA_SUCCESS) { 65731 return result; 65732 } 65733 65734 /* Now open the file. If this fails we don't need to uninitialize the encoder. */ 65735 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); 65736 if (result != MA_SUCCESS) { 65737 return result; 65738 } 65739 65740 pEncoder->data.vfs.pVFS = pVFS; 65741 pEncoder->data.vfs.file = file; 65742 65743 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); 65744 if (result != MA_SUCCESS) { 65745 ma_vfs_or_default_close(pVFS, file); 65746 return result; 65747 } 65748 65749 return MA_SUCCESS; 65750 } 65751 65752 MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65753 { 65754 ma_result result; 65755 ma_vfs_file file; 65756 65757 result = ma_encoder_preinit(pConfig, pEncoder); 65758 if (result != MA_SUCCESS) { 65759 return result; 65760 } 65761 65762 /* Now open the file. If this fails we don't need to uninitialize the encoder. */ 65763 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); 65764 if (result != MA_SUCCESS) { 65765 return result; 65766 } 65767 65768 pEncoder->data.vfs.pVFS = pVFS; 65769 pEncoder->data.vfs.file = file; 65770 65771 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); 65772 if (result != MA_SUCCESS) { 65773 ma_vfs_or_default_close(pVFS, file); 65774 return result; 65775 } 65776 65777 return MA_SUCCESS; 65778 } 65779 65780 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65781 { 65782 return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); 65783 } 65784 65785 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65786 { 65787 return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); 65788 } 65789 65790 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) 65791 { 65792 ma_result result; 65793 65794 result = ma_encoder_preinit(pConfig, pEncoder); 65795 if (result != MA_SUCCESS) { 65796 return result; 65797 } 65798 65799 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); 65800 } 65801 65802 65803 MA_API void ma_encoder_uninit(ma_encoder* pEncoder) 65804 { 65805 if (pEncoder == NULL) { 65806 return; 65807 } 65808 65809 if (pEncoder->onUninit) { 65810 pEncoder->onUninit(pEncoder); 65811 } 65812 65813 /* If we have a file handle, close it. */ 65814 if (pEncoder->onWrite == ma_encoder__on_write_vfs) { 65815 ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); 65816 pEncoder->data.vfs.file = NULL; 65817 } 65818 } 65819 65820 65821 MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) 65822 { 65823 if (pFramesWritten != NULL) { 65824 *pFramesWritten = 0; 65825 } 65826 65827 if (pEncoder == NULL || pFramesIn == NULL) { 65828 return MA_INVALID_ARGS; 65829 } 65830 65831 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); 65832 } 65833 #endif /* MA_NO_ENCODING */ 65834 65835 65836 65837 /************************************************************************************************************************************************************** 65838 65839 Generation 65840 65841 **************************************************************************************************************************************************************/ 65842 #ifndef MA_NO_GENERATION 65843 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) 65844 { 65845 ma_waveform_config config; 65846 65847 MA_ZERO_OBJECT(&config); 65848 config.format = format; 65849 config.channels = channels; 65850 config.sampleRate = sampleRate; 65851 config.type = type; 65852 config.amplitude = amplitude; 65853 config.frequency = frequency; 65854 65855 return config; 65856 } 65857 65858 static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 65859 { 65860 return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); 65861 } 65862 65863 static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 65864 { 65865 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); 65866 } 65867 65868 static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 65869 { 65870 ma_waveform* pWaveform = (ma_waveform*)pDataSource; 65871 65872 *pFormat = pWaveform->config.format; 65873 *pChannels = pWaveform->config.channels; 65874 *pSampleRate = pWaveform->config.sampleRate; 65875 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); 65876 65877 return MA_SUCCESS; 65878 } 65879 65880 static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) 65881 { 65882 ma_waveform* pWaveform = (ma_waveform*)pDataSource; 65883 65884 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); 65885 65886 return MA_SUCCESS; 65887 } 65888 65889 static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) 65890 { 65891 return (1.0 / (sampleRate / frequency)); 65892 } 65893 65894 static void ma_waveform__update_advance(ma_waveform* pWaveform) 65895 { 65896 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); 65897 } 65898 65899 static ma_data_source_vtable g_ma_waveform_data_source_vtable = 65900 { 65901 ma_waveform__data_source_on_read, 65902 ma_waveform__data_source_on_seek, 65903 ma_waveform__data_source_on_get_data_format, 65904 ma_waveform__data_source_on_get_cursor, 65905 NULL, /* onGetLength. There's no notion of a length in waveforms. */ 65906 NULL, /* onSetLooping */ 65907 0 65908 }; 65909 65910 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) 65911 { 65912 ma_result result; 65913 ma_data_source_config dataSourceConfig; 65914 65915 if (pWaveform == NULL) { 65916 return MA_INVALID_ARGS; 65917 } 65918 65919 MA_ZERO_OBJECT(pWaveform); 65920 65921 dataSourceConfig = ma_data_source_config_init(); 65922 dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; 65923 65924 result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); 65925 if (result != MA_SUCCESS) { 65926 return result; 65927 } 65928 65929 pWaveform->config = *pConfig; 65930 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); 65931 pWaveform->time = 0; 65932 65933 return MA_SUCCESS; 65934 } 65935 65936 MA_API void ma_waveform_uninit(ma_waveform* pWaveform) 65937 { 65938 if (pWaveform == NULL) { 65939 return; 65940 } 65941 65942 ma_data_source_uninit(&pWaveform->ds); 65943 } 65944 65945 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) 65946 { 65947 if (pWaveform == NULL) { 65948 return MA_INVALID_ARGS; 65949 } 65950 65951 pWaveform->config.amplitude = amplitude; 65952 return MA_SUCCESS; 65953 } 65954 65955 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) 65956 { 65957 if (pWaveform == NULL) { 65958 return MA_INVALID_ARGS; 65959 } 65960 65961 pWaveform->config.frequency = frequency; 65962 ma_waveform__update_advance(pWaveform); 65963 65964 return MA_SUCCESS; 65965 } 65966 65967 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) 65968 { 65969 if (pWaveform == NULL) { 65970 return MA_INVALID_ARGS; 65971 } 65972 65973 pWaveform->config.type = type; 65974 return MA_SUCCESS; 65975 } 65976 65977 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) 65978 { 65979 if (pWaveform == NULL) { 65980 return MA_INVALID_ARGS; 65981 } 65982 65983 pWaveform->config.sampleRate = sampleRate; 65984 ma_waveform__update_advance(pWaveform); 65985 65986 return MA_SUCCESS; 65987 } 65988 65989 static float ma_waveform_sine_f32(double time, double amplitude) 65990 { 65991 return (float)(ma_sind(MA_TAU_D * time) * amplitude); 65992 } 65993 65994 static ma_int16 ma_waveform_sine_s16(double time, double amplitude) 65995 { 65996 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); 65997 } 65998 65999 static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) 66000 { 66001 double f = time - (ma_int64)time; 66002 double r; 66003 66004 if (f < dutyCycle) { 66005 r = amplitude; 66006 } else { 66007 r = -amplitude; 66008 } 66009 66010 return (float)r; 66011 } 66012 66013 static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) 66014 { 66015 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); 66016 } 66017 66018 static float ma_waveform_triangle_f32(double time, double amplitude) 66019 { 66020 double f = time - (ma_int64)time; 66021 double r; 66022 66023 r = 2 * ma_abs(2 * (f - 0.5)) - 1; 66024 66025 return (float)(r * amplitude); 66026 } 66027 66028 static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) 66029 { 66030 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); 66031 } 66032 66033 static float ma_waveform_sawtooth_f32(double time, double amplitude) 66034 { 66035 double f = time - (ma_int64)time; 66036 double r; 66037 66038 r = 2 * (f - 0.5); 66039 66040 return (float)(r * amplitude); 66041 } 66042 66043 static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) 66044 { 66045 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); 66046 } 66047 66048 static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) 66049 { 66050 ma_uint64 iFrame; 66051 ma_uint64 iChannel; 66052 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); 66053 ma_uint32 bpf = bps * pWaveform->config.channels; 66054 66055 MA_ASSERT(pWaveform != NULL); 66056 MA_ASSERT(pFramesOut != NULL); 66057 66058 if (pWaveform->config.format == ma_format_f32) { 66059 float* pFramesOutF32 = (float*)pFramesOut; 66060 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66061 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); 66062 pWaveform->time += pWaveform->advance; 66063 66064 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66065 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; 66066 } 66067 } 66068 } else if (pWaveform->config.format == ma_format_s16) { 66069 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66070 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66071 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); 66072 pWaveform->time += pWaveform->advance; 66073 66074 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66075 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; 66076 } 66077 } 66078 } else { 66079 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66080 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); 66081 pWaveform->time += pWaveform->advance; 66082 66083 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66084 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66085 } 66086 } 66087 } 66088 } 66089 66090 static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) 66091 { 66092 ma_uint64 iFrame; 66093 ma_uint64 iChannel; 66094 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); 66095 ma_uint32 bpf = bps * pWaveform->config.channels; 66096 66097 MA_ASSERT(pWaveform != NULL); 66098 MA_ASSERT(pFramesOut != NULL); 66099 66100 if (pWaveform->config.format == ma_format_f32) { 66101 float* pFramesOutF32 = (float*)pFramesOut; 66102 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66103 float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); 66104 pWaveform->time += pWaveform->advance; 66105 66106 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66107 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; 66108 } 66109 } 66110 } else if (pWaveform->config.format == ma_format_s16) { 66111 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66112 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66113 ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); 66114 pWaveform->time += pWaveform->advance; 66115 66116 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66117 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; 66118 } 66119 } 66120 } else { 66121 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66122 float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); 66123 pWaveform->time += pWaveform->advance; 66124 66125 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66126 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66127 } 66128 } 66129 } 66130 } 66131 66132 static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) 66133 { 66134 ma_uint64 iFrame; 66135 ma_uint64 iChannel; 66136 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); 66137 ma_uint32 bpf = bps * pWaveform->config.channels; 66138 66139 MA_ASSERT(pWaveform != NULL); 66140 MA_ASSERT(pFramesOut != NULL); 66141 66142 if (pWaveform->config.format == ma_format_f32) { 66143 float* pFramesOutF32 = (float*)pFramesOut; 66144 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66145 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); 66146 pWaveform->time += pWaveform->advance; 66147 66148 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66149 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; 66150 } 66151 } 66152 } else if (pWaveform->config.format == ma_format_s16) { 66153 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66154 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66155 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); 66156 pWaveform->time += pWaveform->advance; 66157 66158 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66159 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; 66160 } 66161 } 66162 } else { 66163 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66164 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); 66165 pWaveform->time += pWaveform->advance; 66166 66167 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66168 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66169 } 66170 } 66171 } 66172 } 66173 66174 static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) 66175 { 66176 ma_uint64 iFrame; 66177 ma_uint64 iChannel; 66178 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); 66179 ma_uint32 bpf = bps * pWaveform->config.channels; 66180 66181 MA_ASSERT(pWaveform != NULL); 66182 MA_ASSERT(pFramesOut != NULL); 66183 66184 if (pWaveform->config.format == ma_format_f32) { 66185 float* pFramesOutF32 = (float*)pFramesOut; 66186 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66187 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); 66188 pWaveform->time += pWaveform->advance; 66189 66190 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66191 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; 66192 } 66193 } 66194 } else if (pWaveform->config.format == ma_format_s16) { 66195 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66196 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66197 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); 66198 pWaveform->time += pWaveform->advance; 66199 66200 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66201 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; 66202 } 66203 } 66204 } else { 66205 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66206 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); 66207 pWaveform->time += pWaveform->advance; 66208 66209 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { 66210 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66211 } 66212 } 66213 } 66214 } 66215 66216 MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 66217 { 66218 if (pFramesRead != NULL) { 66219 *pFramesRead = 0; 66220 } 66221 66222 if (frameCount == 0) { 66223 return MA_INVALID_ARGS; 66224 } 66225 66226 if (pWaveform == NULL) { 66227 return MA_INVALID_ARGS; 66228 } 66229 66230 if (pFramesOut != NULL) { 66231 switch (pWaveform->config.type) 66232 { 66233 case ma_waveform_type_sine: 66234 { 66235 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); 66236 } break; 66237 66238 case ma_waveform_type_square: 66239 { 66240 ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); 66241 } break; 66242 66243 case ma_waveform_type_triangle: 66244 { 66245 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); 66246 } break; 66247 66248 case ma_waveform_type_sawtooth: 66249 { 66250 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); 66251 } break; 66252 66253 default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ 66254 } 66255 } else { 66256 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ 66257 } 66258 66259 if (pFramesRead != NULL) { 66260 *pFramesRead = frameCount; 66261 } 66262 66263 return MA_SUCCESS; 66264 } 66265 66266 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) 66267 { 66268 if (pWaveform == NULL) { 66269 return MA_INVALID_ARGS; 66270 } 66271 66272 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ 66273 66274 return MA_SUCCESS; 66275 } 66276 66277 MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) 66278 { 66279 ma_pulsewave_config config; 66280 66281 MA_ZERO_OBJECT(&config); 66282 config.format = format; 66283 config.channels = channels; 66284 config.sampleRate = sampleRate; 66285 config.dutyCycle = dutyCycle; 66286 config.amplitude = amplitude; 66287 config.frequency = frequency; 66288 66289 return config; 66290 } 66291 66292 MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) 66293 { 66294 ma_result result; 66295 ma_waveform_config config; 66296 66297 if (pWaveform == NULL) { 66298 return MA_INVALID_ARGS; 66299 } 66300 66301 MA_ZERO_OBJECT(pWaveform); 66302 66303 config = ma_waveform_config_init( 66304 pConfig->format, 66305 pConfig->channels, 66306 pConfig->sampleRate, 66307 ma_waveform_type_square, 66308 pConfig->amplitude, 66309 pConfig->frequency 66310 ); 66311 66312 result = ma_waveform_init(&config, &pWaveform->waveform); 66313 ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); 66314 66315 return result; 66316 } 66317 66318 MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) 66319 { 66320 if (pWaveform == NULL) { 66321 return; 66322 } 66323 66324 ma_waveform_uninit(&pWaveform->waveform); 66325 } 66326 66327 MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 66328 { 66329 if (pFramesRead != NULL) { 66330 *pFramesRead = 0; 66331 } 66332 66333 if (frameCount == 0) { 66334 return MA_INVALID_ARGS; 66335 } 66336 66337 if (pWaveform == NULL) { 66338 return MA_INVALID_ARGS; 66339 } 66340 66341 if (pFramesOut != NULL) { 66342 ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); 66343 } else { 66344 pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ 66345 } 66346 66347 if (pFramesRead != NULL) { 66348 *pFramesRead = frameCount; 66349 } 66350 66351 return MA_SUCCESS; 66352 } 66353 66354 MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) 66355 { 66356 if (pWaveform == NULL) { 66357 return MA_INVALID_ARGS; 66358 } 66359 66360 ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); 66361 66362 return MA_SUCCESS; 66363 } 66364 66365 MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) 66366 { 66367 if (pWaveform == NULL) { 66368 return MA_INVALID_ARGS; 66369 } 66370 66371 pWaveform->config.amplitude = amplitude; 66372 ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); 66373 66374 return MA_SUCCESS; 66375 } 66376 66377 MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) 66378 { 66379 if (pWaveform == NULL) { 66380 return MA_INVALID_ARGS; 66381 } 66382 66383 pWaveform->config.frequency = frequency; 66384 ma_waveform_set_frequency(&pWaveform->waveform, frequency); 66385 66386 return MA_SUCCESS; 66387 } 66388 66389 MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) 66390 { 66391 if (pWaveform == NULL) { 66392 return MA_INVALID_ARGS; 66393 } 66394 66395 pWaveform->config.sampleRate = sampleRate; 66396 ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); 66397 66398 return MA_SUCCESS; 66399 } 66400 66401 MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) 66402 { 66403 if (pWaveform == NULL) { 66404 return MA_INVALID_ARGS; 66405 } 66406 66407 pWaveform->config.dutyCycle = dutyCycle; 66408 66409 return MA_SUCCESS; 66410 } 66411 66412 66413 66414 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) 66415 { 66416 ma_noise_config config; 66417 MA_ZERO_OBJECT(&config); 66418 66419 config.format = format; 66420 config.channels = channels; 66421 config.type = type; 66422 config.seed = seed; 66423 config.amplitude = amplitude; 66424 66425 if (config.seed == 0) { 66426 config.seed = MA_DEFAULT_LCG_SEED; 66427 } 66428 66429 return config; 66430 } 66431 66432 66433 static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 66434 { 66435 return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); 66436 } 66437 66438 static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) 66439 { 66440 /* No-op. Just pretend to be successful. */ 66441 (void)pDataSource; 66442 (void)frameIndex; 66443 return MA_SUCCESS; 66444 } 66445 66446 static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 66447 { 66448 ma_noise* pNoise = (ma_noise*)pDataSource; 66449 66450 *pFormat = pNoise->config.format; 66451 *pChannels = pNoise->config.channels; 66452 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ 66453 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); 66454 66455 return MA_SUCCESS; 66456 } 66457 66458 static ma_data_source_vtable g_ma_noise_data_source_vtable = 66459 { 66460 ma_noise__data_source_on_read, 66461 ma_noise__data_source_on_seek, /* No-op for noise. */ 66462 ma_noise__data_source_on_get_data_format, 66463 NULL, /* onGetCursor. No notion of a cursor for noise. */ 66464 NULL, /* onGetLength. No notion of a length for noise. */ 66465 NULL, /* onSetLooping */ 66466 0 66467 }; 66468 66469 66470 #ifndef MA_PINK_NOISE_BIN_SIZE 66471 #define MA_PINK_NOISE_BIN_SIZE 16 66472 #endif 66473 66474 typedef struct 66475 { 66476 size_t sizeInBytes; 66477 struct 66478 { 66479 size_t binOffset; 66480 size_t accumulationOffset; 66481 size_t counterOffset; 66482 } pink; 66483 struct 66484 { 66485 size_t accumulationOffset; 66486 } brownian; 66487 } ma_noise_heap_layout; 66488 66489 static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) 66490 { 66491 MA_ASSERT(pHeapLayout != NULL); 66492 66493 MA_ZERO_OBJECT(pHeapLayout); 66494 66495 if (pConfig == NULL) { 66496 return MA_INVALID_ARGS; 66497 } 66498 66499 if (pConfig->channels == 0) { 66500 return MA_INVALID_ARGS; 66501 } 66502 66503 pHeapLayout->sizeInBytes = 0; 66504 66505 /* Pink. */ 66506 if (pConfig->type == ma_noise_type_pink) { 66507 /* bin */ 66508 pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; 66509 pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; 66510 pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; 66511 66512 /* accumulation */ 66513 pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; 66514 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; 66515 66516 /* counter */ 66517 pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; 66518 pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; 66519 } 66520 66521 /* Brownian. */ 66522 if (pConfig->type == ma_noise_type_brownian) { 66523 /* accumulation */ 66524 pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; 66525 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; 66526 } 66527 66528 /* Make sure allocation size is aligned. */ 66529 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 66530 66531 return MA_SUCCESS; 66532 } 66533 66534 MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) 66535 { 66536 ma_result result; 66537 ma_noise_heap_layout heapLayout; 66538 66539 if (pHeapSizeInBytes == NULL) { 66540 return MA_INVALID_ARGS; 66541 } 66542 66543 *pHeapSizeInBytes = 0; 66544 66545 result = ma_noise_get_heap_layout(pConfig, &heapLayout); 66546 if (result != MA_SUCCESS) { 66547 return result; 66548 } 66549 66550 *pHeapSizeInBytes = heapLayout.sizeInBytes; 66551 66552 return MA_SUCCESS; 66553 } 66554 66555 MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) 66556 { 66557 ma_result result; 66558 ma_noise_heap_layout heapLayout; 66559 ma_data_source_config dataSourceConfig; 66560 ma_uint32 iChannel; 66561 66562 if (pNoise == NULL) { 66563 return MA_INVALID_ARGS; 66564 } 66565 66566 MA_ZERO_OBJECT(pNoise); 66567 66568 result = ma_noise_get_heap_layout(pConfig, &heapLayout); 66569 if (result != MA_SUCCESS) { 66570 return result; 66571 } 66572 66573 pNoise->_pHeap = pHeap; 66574 MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); 66575 66576 dataSourceConfig = ma_data_source_config_init(); 66577 dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; 66578 66579 result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); 66580 if (result != MA_SUCCESS) { 66581 return result; 66582 } 66583 66584 pNoise->config = *pConfig; 66585 ma_lcg_seed(&pNoise->lcg, pConfig->seed); 66586 66587 if (pNoise->config.type == ma_noise_type_pink) { 66588 pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); 66589 pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); 66590 pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); 66591 66592 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { 66593 pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); 66594 pNoise->state.pink.accumulation[iChannel] = 0; 66595 pNoise->state.pink.counter[iChannel] = 1; 66596 } 66597 } 66598 66599 if (pNoise->config.type == ma_noise_type_brownian) { 66600 pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); 66601 66602 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { 66603 pNoise->state.brownian.accumulation[iChannel] = 0; 66604 } 66605 } 66606 66607 return MA_SUCCESS; 66608 } 66609 66610 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) 66611 { 66612 ma_result result; 66613 size_t heapSizeInBytes; 66614 void* pHeap; 66615 66616 result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); 66617 if (result != MA_SUCCESS) { 66618 return result; 66619 } 66620 66621 if (heapSizeInBytes > 0) { 66622 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 66623 if (pHeap == NULL) { 66624 return MA_OUT_OF_MEMORY; 66625 } 66626 } else { 66627 pHeap = NULL; 66628 } 66629 66630 result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); 66631 if (result != MA_SUCCESS) { 66632 ma_free(pHeap, pAllocationCallbacks); 66633 return result; 66634 } 66635 66636 pNoise->_ownsHeap = MA_TRUE; 66637 return MA_SUCCESS; 66638 } 66639 66640 MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) 66641 { 66642 if (pNoise == NULL) { 66643 return; 66644 } 66645 66646 ma_data_source_uninit(&pNoise->ds); 66647 66648 if (pNoise->_ownsHeap) { 66649 ma_free(pNoise->_pHeap, pAllocationCallbacks); 66650 } 66651 } 66652 66653 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) 66654 { 66655 if (pNoise == NULL) { 66656 return MA_INVALID_ARGS; 66657 } 66658 66659 pNoise->config.amplitude = amplitude; 66660 return MA_SUCCESS; 66661 } 66662 66663 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) 66664 { 66665 if (pNoise == NULL) { 66666 return MA_INVALID_ARGS; 66667 } 66668 66669 pNoise->lcg.state = seed; 66670 return MA_SUCCESS; 66671 } 66672 66673 66674 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) 66675 { 66676 if (pNoise == NULL) { 66677 return MA_INVALID_ARGS; 66678 } 66679 66680 /* 66681 This function should never have been implemented in the first place. Changing the type dynamically is not 66682 supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function 66683 will be removed in version 0.12. 66684 */ 66685 MA_ASSERT(MA_FALSE); 66686 (void)type; 66687 66688 return MA_INVALID_OPERATION; 66689 } 66690 66691 static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) 66692 { 66693 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); 66694 } 66695 66696 static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) 66697 { 66698 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); 66699 } 66700 66701 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) 66702 { 66703 ma_uint64 iFrame; 66704 ma_uint32 iChannel; 66705 const ma_uint32 channels = pNoise->config.channels; 66706 MA_ASSUME(channels > 0); 66707 66708 if (pNoise->config.format == ma_format_f32) { 66709 float* pFramesOutF32 = (float*)pFramesOut; 66710 if (pNoise->config.duplicateChannels) { 66711 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66712 float s = ma_noise_f32_white(pNoise); 66713 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66714 pFramesOutF32[iFrame*channels + iChannel] = s; 66715 } 66716 } 66717 } else { 66718 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66719 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66720 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); 66721 } 66722 } 66723 } 66724 } else if (pNoise->config.format == ma_format_s16) { 66725 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66726 if (pNoise->config.duplicateChannels) { 66727 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66728 ma_int16 s = ma_noise_s16_white(pNoise); 66729 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66730 pFramesOutS16[iFrame*channels + iChannel] = s; 66731 } 66732 } 66733 } else { 66734 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66735 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66736 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); 66737 } 66738 } 66739 } 66740 } else { 66741 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); 66742 const ma_uint32 bpf = bps * channels; 66743 66744 if (pNoise->config.duplicateChannels) { 66745 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66746 float s = ma_noise_f32_white(pNoise); 66747 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66748 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66749 } 66750 } 66751 } else { 66752 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66753 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66754 float s = ma_noise_f32_white(pNoise); 66755 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66756 } 66757 } 66758 } 66759 } 66760 66761 return frameCount; 66762 } 66763 66764 66765 static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) 66766 { 66767 unsigned int n; 66768 66769 /* Special case for odd numbers since they should happen about half the time. */ 66770 if (x & 0x1) { 66771 return 0; 66772 } 66773 66774 if (x == 0) { 66775 return sizeof(x) << 3; 66776 } 66777 66778 n = 1; 66779 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } 66780 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } 66781 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } 66782 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } 66783 n -= x & 0x00000001; 66784 66785 return n; 66786 } 66787 66788 /* 66789 Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h 66790 66791 This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ 66792 */ 66793 static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) 66794 { 66795 double result; 66796 double binPrev; 66797 double binNext; 66798 unsigned int ibin; 66799 66800 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); 66801 66802 binPrev = pNoise->state.pink.bin[iChannel][ibin]; 66803 binNext = ma_lcg_rand_f64(&pNoise->lcg); 66804 pNoise->state.pink.bin[iChannel][ibin] = binNext; 66805 66806 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); 66807 pNoise->state.pink.counter[iChannel] += 1; 66808 66809 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); 66810 result /= 10; 66811 66812 return (float)(result * pNoise->config.amplitude); 66813 } 66814 66815 static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) 66816 { 66817 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); 66818 } 66819 66820 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) 66821 { 66822 ma_uint64 iFrame; 66823 ma_uint32 iChannel; 66824 const ma_uint32 channels = pNoise->config.channels; 66825 MA_ASSUME(channels > 0); 66826 66827 if (pNoise->config.format == ma_format_f32) { 66828 float* pFramesOutF32 = (float*)pFramesOut; 66829 if (pNoise->config.duplicateChannels) { 66830 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66831 float s = ma_noise_f32_pink(pNoise, 0); 66832 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66833 pFramesOutF32[iFrame*channels + iChannel] = s; 66834 } 66835 } 66836 } else { 66837 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66838 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66839 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); 66840 } 66841 } 66842 } 66843 } else if (pNoise->config.format == ma_format_s16) { 66844 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66845 if (pNoise->config.duplicateChannels) { 66846 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66847 ma_int16 s = ma_noise_s16_pink(pNoise, 0); 66848 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66849 pFramesOutS16[iFrame*channels + iChannel] = s; 66850 } 66851 } 66852 } else { 66853 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66854 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66855 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); 66856 } 66857 } 66858 } 66859 } else { 66860 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); 66861 const ma_uint32 bpf = bps * channels; 66862 66863 if (pNoise->config.duplicateChannels) { 66864 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66865 float s = ma_noise_f32_pink(pNoise, 0); 66866 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66867 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66868 } 66869 } 66870 } else { 66871 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66872 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66873 float s = ma_noise_f32_pink(pNoise, iChannel); 66874 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66875 } 66876 } 66877 } 66878 } 66879 66880 return frameCount; 66881 } 66882 66883 66884 static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) 66885 { 66886 double result; 66887 66888 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); 66889 result /= 1.005; /* Don't escape the -1..1 range on average. */ 66890 66891 pNoise->state.brownian.accumulation[iChannel] = result; 66892 result /= 20; 66893 66894 return (float)(result * pNoise->config.amplitude); 66895 } 66896 66897 static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) 66898 { 66899 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); 66900 } 66901 66902 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) 66903 { 66904 ma_uint64 iFrame; 66905 ma_uint32 iChannel; 66906 const ma_uint32 channels = pNoise->config.channels; 66907 MA_ASSUME(channels > 0); 66908 66909 if (pNoise->config.format == ma_format_f32) { 66910 float* pFramesOutF32 = (float*)pFramesOut; 66911 if (pNoise->config.duplicateChannels) { 66912 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66913 float s = ma_noise_f32_brownian(pNoise, 0); 66914 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66915 pFramesOutF32[iFrame*channels + iChannel] = s; 66916 } 66917 } 66918 } else { 66919 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66920 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66921 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); 66922 } 66923 } 66924 } 66925 } else if (pNoise->config.format == ma_format_s16) { 66926 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; 66927 if (pNoise->config.duplicateChannels) { 66928 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66929 ma_int16 s = ma_noise_s16_brownian(pNoise, 0); 66930 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66931 pFramesOutS16[iFrame*channels + iChannel] = s; 66932 } 66933 } 66934 } else { 66935 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66936 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66937 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); 66938 } 66939 } 66940 } 66941 } else { 66942 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); 66943 const ma_uint32 bpf = bps * channels; 66944 66945 if (pNoise->config.duplicateChannels) { 66946 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66947 float s = ma_noise_f32_brownian(pNoise, 0); 66948 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66949 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66950 } 66951 } 66952 } else { 66953 for (iFrame = 0; iFrame < frameCount; iFrame += 1) { 66954 for (iChannel = 0; iChannel < channels; iChannel += 1) { 66955 float s = ma_noise_f32_brownian(pNoise, iChannel); 66956 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); 66957 } 66958 } 66959 } 66960 } 66961 66962 return frameCount; 66963 } 66964 66965 MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 66966 { 66967 ma_uint64 framesRead = 0; 66968 66969 if (pFramesRead != NULL) { 66970 *pFramesRead = 0; 66971 } 66972 66973 if (frameCount == 0) { 66974 return MA_INVALID_ARGS; 66975 } 66976 66977 if (pNoise == NULL) { 66978 return MA_INVALID_ARGS; 66979 } 66980 66981 /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ 66982 if (pFramesOut == NULL) { 66983 framesRead = frameCount; 66984 } else { 66985 switch (pNoise->config.type) { 66986 case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; 66987 case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; 66988 case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; 66989 default: return MA_INVALID_OPERATION; /* Unknown noise type. */ 66990 } 66991 } 66992 66993 if (pFramesRead != NULL) { 66994 *pFramesRead = framesRead; 66995 } 66996 66997 return MA_SUCCESS; 66998 } 66999 #endif /* MA_NO_GENERATION */ 67000 67001 67002 67003 #ifndef MA_NO_RESOURCE_MANAGER 67004 #ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 67005 #define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 67006 #endif 67007 67008 #ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 67009 #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 67010 #endif 67011 67012 MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) 67013 { 67014 ma_resource_manager_pipeline_notifications notifications; 67015 67016 MA_ZERO_OBJECT(¬ifications); 67017 67018 return notifications; 67019 } 67020 67021 static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) 67022 { 67023 if (pPipelineNotifications == NULL) { 67024 return; 67025 } 67026 67027 if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } 67028 if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } 67029 } 67030 67031 static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) 67032 { 67033 if (pPipelineNotifications == NULL) { 67034 return; 67035 } 67036 67037 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } 67038 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } 67039 } 67040 67041 static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) 67042 { 67043 if (pPipelineNotifications == NULL) { 67044 return; 67045 } 67046 67047 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } 67048 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } 67049 } 67050 67051 67052 67053 #ifndef MA_DEFAULT_HASH_SEED 67054 #define MA_DEFAULT_HASH_SEED 42 67055 #endif 67056 67057 /* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ 67058 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 67059 #pragma GCC diagnostic push 67060 #if __GNUC__ >= 7 67061 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 67062 #endif 67063 #endif 67064 67065 static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) 67066 { 67067 return (x << r) | (x >> (32 - r)); 67068 } 67069 67070 static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) 67071 { 67072 ma_uint32 block; 67073 67074 /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ 67075 MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); 67076 67077 if (ma_is_little_endian()) { 67078 return block; 67079 } else { 67080 return ma_swap_endian_uint32(block); 67081 } 67082 } 67083 67084 static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) 67085 { 67086 h ^= h >> 16; 67087 h *= 0x85ebca6b; 67088 h ^= h >> 13; 67089 h *= 0xc2b2ae35; 67090 h ^= h >> 16; 67091 67092 return h; 67093 } 67094 67095 static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) 67096 { 67097 const ma_uint8* data = (const ma_uint8*)key; 67098 const ma_uint32* blocks; 67099 const ma_uint8* tail; 67100 const int nblocks = len / 4; 67101 ma_uint32 h1 = seed; 67102 ma_uint32 c1 = 0xcc9e2d51; 67103 ma_uint32 c2 = 0x1b873593; 67104 ma_uint32 k1; 67105 int i; 67106 67107 blocks = (const ma_uint32 *)(data + nblocks*4); 67108 67109 for(i = -nblocks; i; i++) { 67110 k1 = ma_hash_getblock(blocks,i); 67111 67112 k1 *= c1; 67113 k1 = ma_rotl32(k1, 15); 67114 k1 *= c2; 67115 67116 h1 ^= k1; 67117 h1 = ma_rotl32(h1, 13); 67118 h1 = h1*5 + 0xe6546b64; 67119 } 67120 67121 67122 tail = (const ma_uint8*)(data + nblocks*4); 67123 67124 k1 = 0; 67125 switch(len & 3) { 67126 case 3: k1 ^= tail[2] << 16; 67127 case 2: k1 ^= tail[1] << 8; 67128 case 1: k1 ^= tail[0]; 67129 k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; 67130 }; 67131 67132 67133 h1 ^= len; 67134 h1 = ma_hash_fmix32(h1); 67135 67136 return h1; 67137 } 67138 67139 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 67140 #pragma GCC diagnostic push 67141 #endif 67142 /* End MurmurHash3 */ 67143 67144 static ma_uint32 ma_hash_string_32(const char* str) 67145 { 67146 return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); 67147 } 67148 67149 static ma_uint32 ma_hash_string_w_32(const wchar_t* str) 67150 { 67151 return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); 67152 } 67153 67154 67155 67156 67157 /* 67158 Basic BST Functions 67159 */ 67160 static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) 67161 { 67162 ma_resource_manager_data_buffer_node* pCurrentNode; 67163 67164 MA_ASSERT(pResourceManager != NULL); 67165 MA_ASSERT(ppDataBufferNode != NULL); 67166 67167 pCurrentNode = pResourceManager->pRootDataBufferNode; 67168 while (pCurrentNode != NULL) { 67169 if (hashedName32 == pCurrentNode->hashedName32) { 67170 break; /* Found. */ 67171 } else if (hashedName32 < pCurrentNode->hashedName32) { 67172 pCurrentNode = pCurrentNode->pChildLo; 67173 } else { 67174 pCurrentNode = pCurrentNode->pChildHi; 67175 } 67176 } 67177 67178 *ppDataBufferNode = pCurrentNode; 67179 67180 if (pCurrentNode == NULL) { 67181 return MA_DOES_NOT_EXIST; 67182 } else { 67183 return MA_SUCCESS; 67184 } 67185 } 67186 67187 static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) 67188 { 67189 ma_result result = MA_SUCCESS; 67190 ma_resource_manager_data_buffer_node* pCurrentNode; 67191 67192 MA_ASSERT(pResourceManager != NULL); 67193 MA_ASSERT(ppInsertPoint != NULL); 67194 67195 *ppInsertPoint = NULL; 67196 67197 if (pResourceManager->pRootDataBufferNode == NULL) { 67198 return MA_SUCCESS; /* No items. */ 67199 } 67200 67201 /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ 67202 pCurrentNode = pResourceManager->pRootDataBufferNode; 67203 while (pCurrentNode != NULL) { 67204 if (hashedName32 == pCurrentNode->hashedName32) { 67205 result = MA_ALREADY_EXISTS; 67206 break; 67207 } else { 67208 if (hashedName32 < pCurrentNode->hashedName32) { 67209 if (pCurrentNode->pChildLo == NULL) { 67210 result = MA_SUCCESS; 67211 break; 67212 } else { 67213 pCurrentNode = pCurrentNode->pChildLo; 67214 } 67215 } else { 67216 if (pCurrentNode->pChildHi == NULL) { 67217 result = MA_SUCCESS; 67218 break; 67219 } else { 67220 pCurrentNode = pCurrentNode->pChildHi; 67221 } 67222 } 67223 } 67224 } 67225 67226 *ppInsertPoint = pCurrentNode; 67227 return result; 67228 } 67229 67230 static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) 67231 { 67232 MA_ASSERT(pResourceManager != NULL); 67233 MA_ASSERT(pDataBufferNode != NULL); 67234 67235 /* The key must have been set before calling this function. */ 67236 MA_ASSERT(pDataBufferNode->hashedName32 != 0); 67237 67238 if (pInsertPoint == NULL) { 67239 /* It's the first node. */ 67240 pResourceManager->pRootDataBufferNode = pDataBufferNode; 67241 } else { 67242 /* It's not the first node. It needs to be inserted. */ 67243 if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { 67244 MA_ASSERT(pInsertPoint->pChildLo == NULL); 67245 pInsertPoint->pChildLo = pDataBufferNode; 67246 } else { 67247 MA_ASSERT(pInsertPoint->pChildHi == NULL); 67248 pInsertPoint->pChildHi = pDataBufferNode; 67249 } 67250 } 67251 67252 pDataBufferNode->pParent = pInsertPoint; 67253 67254 return MA_SUCCESS; 67255 } 67256 67257 #if 0 /* Unused for now. */ 67258 static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) 67259 { 67260 ma_result result; 67261 ma_resource_manager_data_buffer_node* pInsertPoint; 67262 67263 MA_ASSERT(pResourceManager != NULL); 67264 MA_ASSERT(pDataBufferNode != NULL); 67265 67266 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); 67267 if (result != MA_SUCCESS) { 67268 return MA_INVALID_ARGS; 67269 } 67270 67271 return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); 67272 } 67273 #endif 67274 67275 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) 67276 { 67277 ma_resource_manager_data_buffer_node* pCurrentNode; 67278 67279 MA_ASSERT(pDataBufferNode != NULL); 67280 67281 pCurrentNode = pDataBufferNode; 67282 while (pCurrentNode->pChildLo != NULL) { 67283 pCurrentNode = pCurrentNode->pChildLo; 67284 } 67285 67286 return pCurrentNode; 67287 } 67288 67289 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) 67290 { 67291 ma_resource_manager_data_buffer_node* pCurrentNode; 67292 67293 MA_ASSERT(pDataBufferNode != NULL); 67294 67295 pCurrentNode = pDataBufferNode; 67296 while (pCurrentNode->pChildHi != NULL) { 67297 pCurrentNode = pCurrentNode->pChildHi; 67298 } 67299 67300 return pCurrentNode; 67301 } 67302 67303 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) 67304 { 67305 MA_ASSERT(pDataBufferNode != NULL); 67306 MA_ASSERT(pDataBufferNode->pChildHi != NULL); 67307 67308 return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); 67309 } 67310 67311 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) 67312 { 67313 MA_ASSERT(pDataBufferNode != NULL); 67314 MA_ASSERT(pDataBufferNode->pChildLo != NULL); 67315 67316 return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); 67317 } 67318 67319 static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) 67320 { 67321 MA_ASSERT(pResourceManager != NULL); 67322 MA_ASSERT(pDataBufferNode != NULL); 67323 67324 if (pDataBufferNode->pChildLo == NULL) { 67325 if (pDataBufferNode->pChildHi == NULL) { 67326 /* Simple case - deleting a buffer with no children. */ 67327 if (pDataBufferNode->pParent == NULL) { 67328 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ 67329 pResourceManager->pRootDataBufferNode = NULL; 67330 } else { 67331 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { 67332 pDataBufferNode->pParent->pChildLo = NULL; 67333 } else { 67334 pDataBufferNode->pParent->pChildHi = NULL; 67335 } 67336 } 67337 } else { 67338 /* Node has one child - pChildHi != NULL. */ 67339 pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; 67340 67341 if (pDataBufferNode->pParent == NULL) { 67342 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); 67343 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; 67344 } else { 67345 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { 67346 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; 67347 } else { 67348 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; 67349 } 67350 } 67351 } 67352 } else { 67353 if (pDataBufferNode->pChildHi == NULL) { 67354 /* Node has one child - pChildLo != NULL. */ 67355 pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; 67356 67357 if (pDataBufferNode->pParent == NULL) { 67358 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); 67359 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; 67360 } else { 67361 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { 67362 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; 67363 } else { 67364 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; 67365 } 67366 } 67367 } else { 67368 /* Complex case - deleting a node with two children. */ 67369 ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; 67370 67371 /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ 67372 pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); 67373 MA_ASSERT(pReplacementDataBufferNode != NULL); 67374 67375 /* 67376 Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement 67377 node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The 67378 replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the 67379 replacement node and reinserting it into the same position as the deleted node. 67380 */ 67381 MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ 67382 MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ 67383 67384 if (pReplacementDataBufferNode->pChildHi == NULL) { 67385 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { 67386 pReplacementDataBufferNode->pParent->pChildLo = NULL; 67387 } else { 67388 pReplacementDataBufferNode->pParent->pChildHi = NULL; 67389 } 67390 } else { 67391 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; 67392 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { 67393 pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; 67394 } else { 67395 pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; 67396 } 67397 } 67398 67399 67400 /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ 67401 if (pDataBufferNode->pParent != NULL) { 67402 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { 67403 pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; 67404 } else { 67405 pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; 67406 } 67407 } 67408 67409 /* Now need to update the replacement node's pointers. */ 67410 pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; 67411 pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; 67412 pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; 67413 67414 /* Now the children of the replacement node need to have their parent pointers updated. */ 67415 if (pReplacementDataBufferNode->pChildLo != NULL) { 67416 pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; 67417 } 67418 if (pReplacementDataBufferNode->pChildHi != NULL) { 67419 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; 67420 } 67421 67422 /* Now the root node needs to be updated. */ 67423 if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { 67424 pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; 67425 } 67426 } 67427 } 67428 67429 return MA_SUCCESS; 67430 } 67431 67432 #if 0 /* Unused for now. */ 67433 static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) 67434 { 67435 ma_result result; 67436 ma_resource_manager_data_buffer_node* pDataBufferNode; 67437 67438 result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); 67439 if (result != MA_SUCCESS) { 67440 return result; /* Could not find the data buffer. */ 67441 } 67442 67443 return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); 67444 } 67445 #endif 67446 67447 static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) 67448 { 67449 return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); 67450 } 67451 67452 static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) 67453 { 67454 ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); 67455 } 67456 67457 static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) 67458 { 67459 ma_uint32 refCount; 67460 67461 MA_ASSERT(pResourceManager != NULL); 67462 MA_ASSERT(pDataBufferNode != NULL); 67463 67464 (void)pResourceManager; 67465 67466 refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; 67467 67468 if (pNewRefCount != NULL) { 67469 *pNewRefCount = refCount; 67470 } 67471 67472 return MA_SUCCESS; 67473 } 67474 67475 static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) 67476 { 67477 ma_uint32 refCount; 67478 67479 MA_ASSERT(pResourceManager != NULL); 67480 MA_ASSERT(pDataBufferNode != NULL); 67481 67482 (void)pResourceManager; 67483 67484 refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; 67485 67486 if (pNewRefCount != NULL) { 67487 *pNewRefCount = refCount; 67488 } 67489 67490 return MA_SUCCESS; 67491 } 67492 67493 static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) 67494 { 67495 MA_ASSERT(pResourceManager != NULL); 67496 MA_ASSERT(pDataBufferNode != NULL); 67497 67498 if (pDataBufferNode->isDataOwnedByResourceManager) { 67499 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { 67500 ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); 67501 pDataBufferNode->data.backend.encoded.pData = NULL; 67502 pDataBufferNode->data.backend.encoded.sizeInBytes = 0; 67503 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { 67504 ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); 67505 pDataBufferNode->data.backend.decoded.pData = NULL; 67506 pDataBufferNode->data.backend.decoded.totalFrameCount = 0; 67507 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { 67508 ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); 67509 } else { 67510 /* Should never hit this if the node was successfully initialized. */ 67511 MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); 67512 } 67513 } 67514 67515 /* The data buffer itself needs to be freed. */ 67516 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); 67517 } 67518 67519 static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) 67520 { 67521 MA_ASSERT(pDataBufferNode != NULL); 67522 67523 return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ 67524 } 67525 67526 67527 static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) 67528 { 67529 MA_ASSERT(pResourceManager != NULL); 67530 67531 return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; 67532 } 67533 67534 67535 typedef struct 67536 { 67537 union 67538 { 67539 ma_async_notification_event e; 67540 ma_async_notification_poll p; 67541 } backend; /* Must be the first member. */ 67542 ma_resource_manager* pResourceManager; 67543 } ma_resource_manager_inline_notification; 67544 67545 static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) 67546 { 67547 MA_ASSERT(pResourceManager != NULL); 67548 MA_ASSERT(pNotification != NULL); 67549 67550 pNotification->pResourceManager = pResourceManager; 67551 67552 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67553 return ma_async_notification_event_init(&pNotification->backend.e); 67554 } else { 67555 return ma_async_notification_poll_init(&pNotification->backend.p); 67556 } 67557 } 67558 67559 static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) 67560 { 67561 MA_ASSERT(pNotification != NULL); 67562 67563 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { 67564 ma_async_notification_event_uninit(&pNotification->backend.e); 67565 } else { 67566 /* No need to uninitialize a polling notification. */ 67567 } 67568 } 67569 67570 static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) 67571 { 67572 MA_ASSERT(pNotification != NULL); 67573 67574 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { 67575 ma_async_notification_event_wait(&pNotification->backend.e); 67576 } else { 67577 while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { 67578 ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); 67579 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { 67580 break; 67581 } 67582 } 67583 } 67584 } 67585 67586 static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) 67587 { 67588 ma_resource_manager_inline_notification_wait(pNotification); 67589 ma_resource_manager_inline_notification_uninit(pNotification); 67590 } 67591 67592 67593 static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) 67594 { 67595 MA_ASSERT(pResourceManager != NULL); 67596 67597 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67598 #ifndef MA_NO_THREADING 67599 { 67600 ma_mutex_lock(&pResourceManager->dataBufferBSTLock); 67601 } 67602 #else 67603 { 67604 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 67605 } 67606 #endif 67607 } else { 67608 /* Threading not enabled. Do nothing. */ 67609 } 67610 } 67611 67612 static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) 67613 { 67614 MA_ASSERT(pResourceManager != NULL); 67615 67616 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67617 #ifndef MA_NO_THREADING 67618 { 67619 ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); 67620 } 67621 #else 67622 { 67623 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 67624 } 67625 #endif 67626 } else { 67627 /* Threading not enabled. Do nothing. */ 67628 } 67629 } 67630 67631 #ifndef MA_NO_THREADING 67632 static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) 67633 { 67634 ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; 67635 MA_ASSERT(pResourceManager != NULL); 67636 67637 for (;;) { 67638 ma_result result; 67639 ma_job job; 67640 67641 result = ma_resource_manager_next_job(pResourceManager, &job); 67642 if (result != MA_SUCCESS) { 67643 break; 67644 } 67645 67646 /* Terminate if we got a quit message. */ 67647 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { 67648 break; 67649 } 67650 67651 ma_job_process(&job); 67652 } 67653 67654 return (ma_thread_result)0; 67655 } 67656 #endif 67657 67658 MA_API ma_resource_manager_config ma_resource_manager_config_init(void) 67659 { 67660 ma_resource_manager_config config; 67661 67662 MA_ZERO_OBJECT(&config); 67663 config.decodedFormat = ma_format_unknown; 67664 config.decodedChannels = 0; 67665 config.decodedSampleRate = 0; 67666 config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ 67667 config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; 67668 67669 /* Flags. */ 67670 config.flags = 0; 67671 #ifdef MA_NO_THREADING 67672 { 67673 /* Threading is disabled at compile time so disable threading at runtime as well by default. */ 67674 config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; 67675 config.jobThreadCount = 0; 67676 } 67677 #endif 67678 67679 return config; 67680 } 67681 67682 67683 MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) 67684 { 67685 ma_result result; 67686 ma_job_queue_config jobQueueConfig; 67687 67688 if (pResourceManager == NULL) { 67689 return MA_INVALID_ARGS; 67690 } 67691 67692 MA_ZERO_OBJECT(pResourceManager); 67693 67694 if (pConfig == NULL) { 67695 return MA_INVALID_ARGS; 67696 } 67697 67698 #ifndef MA_NO_THREADING 67699 { 67700 if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { 67701 return MA_INVALID_ARGS; /* Requesting too many job threads. */ 67702 } 67703 } 67704 #endif 67705 67706 pResourceManager->config = *pConfig; 67707 ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); 67708 67709 /* Get the log set up early so we can start using it as soon as possible. */ 67710 if (pResourceManager->config.pLog == NULL) { 67711 result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); 67712 if (result == MA_SUCCESS) { 67713 pResourceManager->config.pLog = &pResourceManager->log; 67714 } else { 67715 pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ 67716 } 67717 } 67718 67719 if (pResourceManager->config.pVFS == NULL) { 67720 result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); 67721 if (result != MA_SUCCESS) { 67722 return result; /* Failed to initialize the default file system. */ 67723 } 67724 67725 pResourceManager->config.pVFS = &pResourceManager->defaultVFS; 67726 } 67727 67728 /* If threading has been disabled at compile time, enfore it at run time as well. */ 67729 #ifdef MA_NO_THREADING 67730 { 67731 pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; 67732 } 67733 #endif 67734 67735 /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ 67736 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { 67737 pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; 67738 67739 /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ 67740 if (pResourceManager->config.jobThreadCount > 0) { 67741 return MA_INVALID_ARGS; 67742 } 67743 } 67744 67745 /* Job queue. */ 67746 jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; 67747 jobQueueConfig.flags = 0; 67748 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { 67749 if (pResourceManager->config.jobThreadCount > 0) { 67750 return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ 67751 } 67752 67753 jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; 67754 } 67755 67756 result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); 67757 if (result != MA_SUCCESS) { 67758 return result; 67759 } 67760 67761 67762 /* Custom decoding backends. */ 67763 if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { 67764 size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; 67765 67766 pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); 67767 if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { 67768 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); 67769 return MA_OUT_OF_MEMORY; 67770 } 67771 67772 MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); 67773 67774 pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; 67775 pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; 67776 } 67777 67778 67779 67780 /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ 67781 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67782 #ifndef MA_NO_THREADING 67783 { 67784 ma_uint32 iJobThread; 67785 67786 /* Data buffer lock. */ 67787 result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); 67788 if (result != MA_SUCCESS) { 67789 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); 67790 return result; 67791 } 67792 67793 /* Create the job threads last to ensure the threads has access to valid data. */ 67794 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { 67795 result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); 67796 if (result != MA_SUCCESS) { 67797 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); 67798 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); 67799 return result; 67800 } 67801 } 67802 } 67803 #else 67804 { 67805 /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ 67806 MA_ASSERT(MA_FALSE); 67807 } 67808 #endif 67809 } 67810 67811 return MA_SUCCESS; 67812 } 67813 67814 67815 static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) 67816 { 67817 MA_ASSERT(pResourceManager); 67818 67819 /* If everything was done properly, there shouldn't be any active data buffers. */ 67820 while (pResourceManager->pRootDataBufferNode != NULL) { 67821 ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; 67822 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); 67823 67824 /* The data buffer has been removed from the BST, so now we need to free it's data. */ 67825 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); 67826 } 67827 } 67828 67829 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) 67830 { 67831 if (pResourceManager == NULL) { 67832 return; 67833 } 67834 67835 /* 67836 Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the 67837 queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. 67838 */ 67839 ma_resource_manager_post_job_quit(pResourceManager); 67840 67841 /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ 67842 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67843 #ifndef MA_NO_THREADING 67844 { 67845 ma_uint32 iJobThread; 67846 67847 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { 67848 ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); 67849 } 67850 } 67851 #else 67852 { 67853 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 67854 } 67855 #endif 67856 } 67857 67858 /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ 67859 ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); 67860 67861 /* The job queue is no longer needed. */ 67862 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); 67863 67864 /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ 67865 if (ma_resource_manager_is_threading_enabled(pResourceManager)) { 67866 #ifndef MA_NO_THREADING 67867 { 67868 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); 67869 } 67870 #else 67871 { 67872 MA_ASSERT(MA_FALSE); /* Should never hit this. */ 67873 } 67874 #endif 67875 } 67876 67877 ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); 67878 67879 if (pResourceManager->config.pLog == &pResourceManager->log) { 67880 ma_log_uninit(&pResourceManager->log); 67881 } 67882 } 67883 67884 MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) 67885 { 67886 if (pResourceManager == NULL) { 67887 return NULL; 67888 } 67889 67890 return pResourceManager->config.pLog; 67891 } 67892 67893 67894 67895 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) 67896 { 67897 ma_resource_manager_data_source_config config; 67898 67899 MA_ZERO_OBJECT(&config); 67900 config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; 67901 config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; 67902 config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; 67903 config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; 67904 config.isLooping = MA_FALSE; 67905 67906 return config; 67907 } 67908 67909 67910 static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) 67911 { 67912 ma_decoder_config config; 67913 67914 config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); 67915 config.allocationCallbacks = pResourceManager->config.allocationCallbacks; 67916 config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; 67917 config.customBackendCount = pResourceManager->config.customDecodingBackendCount; 67918 config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; 67919 67920 return config; 67921 } 67922 67923 static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) 67924 { 67925 ma_result result; 67926 ma_decoder_config config; 67927 67928 MA_ASSERT(pResourceManager != NULL); 67929 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); 67930 MA_ASSERT(pDecoder != NULL); 67931 67932 config = ma_resource_manager__init_decoder_config(pResourceManager); 67933 67934 if (pFilePath != NULL) { 67935 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); 67936 if (result != MA_SUCCESS) { 67937 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); 67938 return result; 67939 } 67940 } else { 67941 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); 67942 if (result != MA_SUCCESS) { 67943 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) 67944 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); 67945 #endif 67946 return result; 67947 } 67948 } 67949 67950 return MA_SUCCESS; 67951 } 67952 67953 static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) 67954 { 67955 return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); 67956 } 67957 67958 static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) 67959 { 67960 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { 67961 return NULL; /* Connector not yet initialized. */ 67962 } 67963 67964 switch (pDataBuffer->pNode->data.type) 67965 { 67966 case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; 67967 case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; 67968 case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; 67969 67970 case ma_resource_manager_data_supply_type_unknown: 67971 default: 67972 { 67973 ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); 67974 return NULL; 67975 }; 67976 }; 67977 } 67978 67979 static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) 67980 { 67981 ma_result result; 67982 67983 MA_ASSERT(pDataBuffer != NULL); 67984 MA_ASSERT(pConfig != NULL); 67985 MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); 67986 67987 /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ 67988 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); 67989 if (result != MA_SUCCESS && result != MA_BUSY) { 67990 return result; /* The data buffer is in an erroneous state. */ 67991 } 67992 67993 /* 67994 We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the 67995 "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use 67996 an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. 67997 */ 67998 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) 67999 { 68000 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ 68001 { 68002 ma_decoder_config config; 68003 config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); 68004 result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); 68005 } break; 68006 68007 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ 68008 { 68009 ma_audio_buffer_config config; 68010 config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); 68011 result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); 68012 } break; 68013 68014 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ 68015 { 68016 ma_paged_audio_buffer_config config; 68017 config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); 68018 result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); 68019 } break; 68020 68021 case ma_resource_manager_data_supply_type_unknown: 68022 default: 68023 { 68024 /* Unknown data supply type. Should never happen. Need to post an error here. */ 68025 return MA_INVALID_ARGS; 68026 }; 68027 } 68028 68029 /* 68030 Initialization of the connector is when we can fire the init notification. This will give the application access to 68031 the format/channels/rate of the data source. 68032 */ 68033 if (result == MA_SUCCESS) { 68034 /* 68035 The resource manager supports the ability to set the range and loop settings via a config at 68036 initialization time. This results in an case where the ranges could be set explicitly via 68037 ma_data_source_set_*() before we get to this point here. If this happens, we'll end up 68038 hitting a case where we just override those settings which results in what feels like a bug. 68039 68040 To address this we only change the relevant properties if they're not equal to defaults. If 68041 they're equal to defaults there's no need to change them anyway. If they're *not* set to the 68042 default values, we can assume the user has set the range and loop settings via the config. If 68043 they're doing their own calls to ma_data_source_set_*() in addition to setting them via the 68044 config, that's entirely on the caller and any synchronization issue becomes their problem. 68045 */ 68046 if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { 68047 ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); 68048 } 68049 68050 if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { 68051 ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); 68052 } 68053 68054 if (pConfig->isLooping != MA_FALSE) { 68055 ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); 68056 } 68057 68058 ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); 68059 68060 if (pInitNotification != NULL) { 68061 ma_async_notification_signal(pInitNotification); 68062 } 68063 68064 if (pInitFence != NULL) { 68065 ma_fence_release(pInitFence); 68066 } 68067 } 68068 68069 /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ 68070 return result; 68071 } 68072 68073 static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) 68074 { 68075 MA_ASSERT(pResourceManager != NULL); 68076 MA_ASSERT(pDataBuffer != NULL); 68077 68078 (void)pResourceManager; 68079 68080 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) 68081 { 68082 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ 68083 { 68084 ma_decoder_uninit(&pDataBuffer->connector.decoder); 68085 } break; 68086 68087 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ 68088 { 68089 ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); 68090 } break; 68091 68092 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ 68093 { 68094 ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); 68095 } break; 68096 68097 case ma_resource_manager_data_supply_type_unknown: 68098 default: 68099 { 68100 /* Unknown data supply type. Should never happen. Need to post an error here. */ 68101 return MA_INVALID_ARGS; 68102 }; 68103 } 68104 68105 return MA_SUCCESS; 68106 } 68107 68108 static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) 68109 { 68110 MA_ASSERT(pDataBufferNode != NULL); 68111 return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); 68112 } 68113 68114 static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) 68115 { 68116 ma_result result; 68117 size_t dataSizeInBytes; 68118 void* pData; 68119 68120 MA_ASSERT(pResourceManager != NULL); 68121 MA_ASSERT(pDataBufferNode != NULL); 68122 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); 68123 68124 result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); 68125 if (result != MA_SUCCESS) { 68126 if (pFilePath != NULL) { 68127 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); 68128 } else { 68129 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) 68130 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); 68131 #endif 68132 } 68133 68134 return result; 68135 } 68136 68137 pDataBufferNode->data.backend.encoded.pData = pData; 68138 pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; 68139 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ 68140 68141 return MA_SUCCESS; 68142 } 68143 68144 static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) 68145 { 68146 ma_result result = MA_SUCCESS; 68147 ma_decoder* pDecoder; 68148 ma_uint64 totalFrameCount; 68149 68150 MA_ASSERT(pResourceManager != NULL); 68151 MA_ASSERT(pDataBufferNode != NULL); 68152 MA_ASSERT(ppDecoder != NULL); 68153 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); 68154 68155 *ppDecoder = NULL; /* For safety. */ 68156 68157 pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); 68158 if (pDecoder == NULL) { 68159 return MA_OUT_OF_MEMORY; 68160 } 68161 68162 result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); 68163 if (result != MA_SUCCESS) { 68164 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 68165 return result; 68166 } 68167 68168 /* 68169 At this point we have the decoder and we now need to initialize the data supply. This will 68170 be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap 68171 allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter 68172 is used when the length of a sound is unknown until a full decode has been performed. 68173 */ 68174 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { 68175 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); 68176 if (result != MA_SUCCESS) { 68177 return result; 68178 } 68179 } else { 68180 totalFrameCount = 0; 68181 } 68182 68183 if (totalFrameCount > 0) { 68184 /* It's a known length. The data supply is a regular decoded buffer. */ 68185 ma_uint64 dataSizeInBytes; 68186 void* pData; 68187 68188 dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); 68189 if (dataSizeInBytes > MA_SIZE_MAX) { 68190 ma_decoder_uninit(pDecoder); 68191 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 68192 return MA_TOO_BIG; 68193 } 68194 68195 pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); 68196 if (pData == NULL) { 68197 ma_decoder_uninit(pDecoder); 68198 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 68199 return MA_OUT_OF_MEMORY; 68200 } 68201 68202 /* The buffer needs to be initialized to silence in case the caller reads from it. */ 68203 ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); 68204 68205 /* Data has been allocated and the data supply can now be initialized. */ 68206 pDataBufferNode->data.backend.decoded.pData = pData; 68207 pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; 68208 pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; 68209 pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; 68210 pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; 68211 pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; 68212 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ 68213 } else { 68214 /* 68215 It's an unknown length. The data supply is a paged decoded buffer. Setting this up is 68216 actually easier than the non-paged decoded buffer because we just need to initialize 68217 a ma_paged_audio_buffer object. 68218 */ 68219 result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); 68220 if (result != MA_SUCCESS) { 68221 ma_decoder_uninit(pDecoder); 68222 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 68223 return result; 68224 } 68225 68226 pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; 68227 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; 68228 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ 68229 } 68230 68231 *ppDecoder = pDecoder; 68232 68233 return MA_SUCCESS; 68234 } 68235 68236 static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) 68237 { 68238 ma_result result = MA_SUCCESS; 68239 ma_uint64 pageSizeInFrames; 68240 ma_uint64 framesToTryReading; 68241 ma_uint64 framesRead; 68242 68243 MA_ASSERT(pResourceManager != NULL); 68244 MA_ASSERT(pDataBufferNode != NULL); 68245 MA_ASSERT(pDecoder != NULL); 68246 68247 /* We need to know the size of a page in frames to know how many frames to decode. */ 68248 pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); 68249 framesToTryReading = pageSizeInFrames; 68250 68251 /* 68252 Here is where we do the decoding of the next page. We'll run a slightly different path depending 68253 on whether or not we're using a flat or paged buffer because the allocation of the page differs 68254 between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged 68255 buffer, we need to allocate a new page and attach it to the linked list. 68256 */ 68257 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) 68258 { 68259 case ma_resource_manager_data_supply_type_decoded: 68260 { 68261 /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ 68262 void* pDst; 68263 ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; 68264 if (framesToTryReading > framesRemaining) { 68265 framesToTryReading = framesRemaining; 68266 } 68267 68268 if (framesToTryReading > 0) { 68269 pDst = ma_offset_ptr( 68270 pDataBufferNode->data.backend.decoded.pData, 68271 pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) 68272 ); 68273 MA_ASSERT(pDst != NULL); 68274 68275 result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); 68276 if (framesRead > 0) { 68277 pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; 68278 } 68279 } else { 68280 framesRead = 0; 68281 } 68282 } break; 68283 68284 case ma_resource_manager_data_supply_type_decoded_paged: 68285 { 68286 /* The destination buffer is a freshly allocated page. */ 68287 ma_paged_audio_buffer_page* pPage; 68288 68289 result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); 68290 if (result != MA_SUCCESS) { 68291 return result; 68292 } 68293 68294 result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); 68295 if (framesRead > 0) { 68296 pPage->sizeInFrames = framesRead; 68297 68298 result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); 68299 if (result == MA_SUCCESS) { 68300 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; 68301 } else { 68302 /* Failed to append the page. Just abort and set the status to MA_AT_END. */ 68303 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); 68304 result = MA_AT_END; 68305 } 68306 } else { 68307 /* No frames were read. Free the page and just set the status to MA_AT_END. */ 68308 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); 68309 result = MA_AT_END; 68310 } 68311 } break; 68312 68313 case ma_resource_manager_data_supply_type_encoded: 68314 case ma_resource_manager_data_supply_type_unknown: 68315 default: 68316 { 68317 /* Unexpected data supply type. */ 68318 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); 68319 return MA_ERROR; 68320 }; 68321 } 68322 68323 if (result == MA_SUCCESS && framesRead == 0) { 68324 result = MA_AT_END; 68325 } 68326 68327 return result; 68328 } 68329 68330 static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) 68331 { 68332 ma_result result = MA_SUCCESS; 68333 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; 68334 ma_resource_manager_data_buffer_node* pInsertPoint; 68335 68336 if (ppDataBufferNode != NULL) { 68337 *ppDataBufferNode = NULL; 68338 } 68339 68340 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); 68341 if (result == MA_ALREADY_EXISTS) { 68342 /* The node already exists. We just need to increment the reference count. */ 68343 pDataBufferNode = pInsertPoint; 68344 68345 result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); 68346 if (result != MA_SUCCESS) { 68347 return result; /* Should never happen. Failed to increment the reference count. */ 68348 } 68349 68350 result = MA_ALREADY_EXISTS; 68351 goto done; 68352 } else { 68353 /* 68354 The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This 68355 needs to be done inside the critical section to ensure an uninitialization of the node 68356 does not occur before initialization on another thread. 68357 */ 68358 pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); 68359 if (pDataBufferNode == NULL) { 68360 return MA_OUT_OF_MEMORY; 68361 } 68362 68363 MA_ZERO_OBJECT(pDataBufferNode); 68364 pDataBufferNode->hashedName32 = hashedName32; 68365 pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ 68366 68367 if (pExistingData == NULL) { 68368 pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ 68369 pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ 68370 pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; 68371 } else { 68372 pDataBufferNode->data = *pExistingData; 68373 pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ 68374 pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; 68375 } 68376 68377 result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); 68378 if (result != MA_SUCCESS) { 68379 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); 68380 return result; /* Should never happen. Failed to insert the data buffer into the BST. */ 68381 } 68382 68383 /* 68384 Here is where we'll post the job, but only if we're loading asynchronously. If we're 68385 loading synchronously we'll defer loading to a later stage, outside of the critical 68386 section. 68387 */ 68388 if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { 68389 /* Loading asynchronously. Post the job. */ 68390 ma_job job; 68391 char* pFilePathCopy = NULL; 68392 wchar_t* pFilePathWCopy = NULL; 68393 68394 /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ 68395 if (pFilePath != NULL) { 68396 pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); 68397 } else { 68398 pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); 68399 } 68400 68401 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { 68402 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); 68403 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); 68404 return MA_OUT_OF_MEMORY; 68405 } 68406 68407 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68408 ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); 68409 } 68410 68411 /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ 68412 if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } 68413 if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } 68414 68415 /* We now have everything we need to post the job to the job thread. */ 68416 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); 68417 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); 68418 job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; 68419 job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; 68420 job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; 68421 job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; 68422 job.data.resourceManager.loadDataBufferNode.flags = flags; 68423 job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; 68424 job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; 68425 job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; 68426 job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; 68427 68428 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68429 result = ma_job_process(&job); 68430 } else { 68431 result = ma_resource_manager_post_job(pResourceManager, &job); 68432 } 68433 68434 if (result != MA_SUCCESS) { 68435 /* Failed to post job. Probably ran out of memory. */ 68436 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); 68437 68438 /* 68439 Fences were acquired before posting the job, but since the job was not able to 68440 be posted, we need to make sure we release them so nothing gets stuck waiting. 68441 */ 68442 if (pInitFence != NULL) { ma_fence_release(pInitFence); } 68443 if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } 68444 68445 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68446 ma_resource_manager_inline_notification_uninit(pInitNotification); 68447 } else { 68448 /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ 68449 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); 68450 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); 68451 } 68452 68453 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); 68454 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); 68455 68456 return result; 68457 } 68458 } 68459 } 68460 68461 done: 68462 if (ppDataBufferNode != NULL) { 68463 *ppDataBufferNode = pDataBufferNode; 68464 } 68465 68466 return result; 68467 } 68468 68469 static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) 68470 { 68471 ma_result result = MA_SUCCESS; 68472 ma_bool32 nodeAlreadyExists = MA_FALSE; 68473 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; 68474 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ 68475 68476 if (ppDataBufferNode != NULL) { 68477 *ppDataBufferNode = NULL; /* Safety. */ 68478 } 68479 68480 if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { 68481 return MA_INVALID_ARGS; 68482 } 68483 68484 /* If we're specifying existing data, it must be valid. */ 68485 if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { 68486 return MA_INVALID_ARGS; 68487 } 68488 68489 /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ 68490 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { 68491 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; 68492 } 68493 68494 if (hashedName32 == 0) { 68495 if (pFilePath != NULL) { 68496 hashedName32 = ma_hash_string_32(pFilePath); 68497 } else { 68498 hashedName32 = ma_hash_string_w_32(pFilePathW); 68499 } 68500 } 68501 68502 /* 68503 Here is where we either increment the node's reference count or allocate a new one and add it 68504 to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is 68505 posted inside the critical section just in case the caller immediately uninitializes the node 68506 as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the 68507 node is not uninitialized before initialization. 68508 */ 68509 ma_resource_manager_data_buffer_bst_lock(pResourceManager); 68510 { 68511 result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); 68512 } 68513 ma_resource_manager_data_buffer_bst_unlock(pResourceManager); 68514 68515 if (result == MA_ALREADY_EXISTS) { 68516 nodeAlreadyExists = MA_TRUE; 68517 result = MA_SUCCESS; 68518 } else { 68519 if (result != MA_SUCCESS) { 68520 return result; 68521 } 68522 } 68523 68524 /* 68525 If we're loading synchronously, we'll need to load everything now. When loading asynchronously, 68526 a job will have been posted inside the BST critical section so that an uninitialization can be 68527 allocated an appropriate execution order thereby preventing it from being uninitialized before 68528 the node is initialized by the decoding thread(s). 68529 */ 68530 if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ 68531 if (pFilePath == NULL && pFilePathW == NULL) { 68532 /* 68533 If this path is hit, it means a buffer is being copied (i.e. initialized from only the 68534 hashed name), but that node has been freed in the meantime, probably from some other 68535 thread. This is an invalid operation. 68536 */ 68537 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); 68538 result = MA_INVALID_OPERATION; 68539 goto done; 68540 } 68541 68542 if (pDataBufferNode->isDataOwnedByResourceManager) { 68543 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { 68544 /* Loading synchronously. Load the sound in it's entirety here. */ 68545 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { 68546 /* No decoding. This is the simple case - just store the file contents in memory. */ 68547 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); 68548 if (result != MA_SUCCESS) { 68549 goto done; 68550 } 68551 } else { 68552 /* Decoding. We do this the same way as we do when loading asynchronously. */ 68553 ma_decoder* pDecoder; 68554 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); 68555 if (result != MA_SUCCESS) { 68556 goto done; 68557 } 68558 68559 /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ 68560 for (;;) { 68561 /* Decode next page. */ 68562 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); 68563 if (result != MA_SUCCESS) { 68564 break; /* Will return MA_AT_END when the last page has been decoded. */ 68565 } 68566 } 68567 68568 /* Reaching the end needs to be considered successful. */ 68569 if (result == MA_AT_END) { 68570 result = MA_SUCCESS; 68571 } 68572 68573 /* 68574 At this point the data buffer is either fully decoded or some error occurred. Either 68575 way, the decoder is no longer necessary. 68576 */ 68577 ma_decoder_uninit(pDecoder); 68578 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 68579 } 68580 68581 /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ 68582 ma_atomic_exchange_i32(&pDataBufferNode->result, result); 68583 } else { 68584 /* Loading asynchronously. We may need to wait for initialization. */ 68585 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68586 ma_resource_manager_inline_notification_wait(&initNotification); 68587 } 68588 } 68589 } else { 68590 /* The data is not managed by the resource manager so there's nothing else to do. */ 68591 MA_ASSERT(pExistingData != NULL); 68592 } 68593 } 68594 68595 done: 68596 /* If we failed to initialize the data buffer we need to free it. */ 68597 if (result != MA_SUCCESS) { 68598 if (nodeAlreadyExists == MA_FALSE) { 68599 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); 68600 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); 68601 } 68602 } 68603 68604 /* 68605 The init notification needs to be uninitialized. This will be used if the node does not already 68606 exist, and we've specified ASYNC | WAIT_INIT. 68607 */ 68608 if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { 68609 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68610 ma_resource_manager_inline_notification_uninit(&initNotification); 68611 } 68612 } 68613 68614 if (ppDataBufferNode != NULL) { 68615 *ppDataBufferNode = pDataBufferNode; 68616 } 68617 68618 return result; 68619 } 68620 68621 static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) 68622 { 68623 ma_result result = MA_SUCCESS; 68624 ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ 68625 ma_uint32 hashedName32 = 0; 68626 68627 if (pResourceManager == NULL) { 68628 return MA_INVALID_ARGS; 68629 } 68630 68631 if (pDataBufferNode == NULL) { 68632 if (pName == NULL && pNameW == NULL) { 68633 return MA_INVALID_ARGS; 68634 } 68635 68636 if (pName != NULL) { 68637 hashedName32 = ma_hash_string_32(pName); 68638 } else { 68639 hashedName32 = ma_hash_string_w_32(pNameW); 68640 } 68641 } 68642 68643 /* 68644 The first thing to do is decrement the reference counter of the node. Then, if the reference 68645 count is zero, we need to free the node. If the node is still in the process of loading, we'll 68646 need to post a job to the job queue to free the node. Otherwise we'll just do it here. 68647 */ 68648 ma_resource_manager_data_buffer_bst_lock(pResourceManager); 68649 { 68650 /* Might need to find the node. Must be done inside the critical section. */ 68651 if (pDataBufferNode == NULL) { 68652 result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); 68653 if (result != MA_SUCCESS) { 68654 goto stage2; /* Couldn't find the node. */ 68655 } 68656 } 68657 68658 result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); 68659 if (result != MA_SUCCESS) { 68660 goto stage2; /* Should never happen. */ 68661 } 68662 68663 if (refCount == 0) { 68664 result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); 68665 if (result != MA_SUCCESS) { 68666 goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ 68667 } 68668 } 68669 } 68670 ma_resource_manager_data_buffer_bst_unlock(pResourceManager); 68671 68672 stage2: 68673 if (result != MA_SUCCESS) { 68674 return result; 68675 } 68676 68677 /* 68678 Here is where we need to free the node. We don't want to do this inside the critical section 68679 above because we want to keep that as small as possible for multi-threaded efficiency. 68680 */ 68681 if (refCount == 0) { 68682 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { 68683 /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ 68684 ma_job job; 68685 68686 /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ 68687 ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); 68688 68689 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); 68690 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); 68691 job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; 68692 job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; 68693 68694 result = ma_resource_manager_post_job(pResourceManager, &job); 68695 if (result != MA_SUCCESS) { 68696 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); 68697 return result; 68698 } 68699 68700 /* If we don't support threading, process the job queue here. */ 68701 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { 68702 while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { 68703 result = ma_resource_manager_process_next_job(pResourceManager); 68704 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { 68705 result = MA_SUCCESS; 68706 break; 68707 } 68708 } 68709 } else { 68710 /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ 68711 } 68712 } else { 68713 /* The sound isn't loading so we can just free the node here. */ 68714 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); 68715 } 68716 } 68717 68718 return result; 68719 } 68720 68721 68722 68723 static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) 68724 { 68725 MA_ASSERT(pDataBuffer != NULL); 68726 return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); 68727 } 68728 68729 static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 68730 { 68731 return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); 68732 } 68733 68734 static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) 68735 { 68736 return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); 68737 } 68738 68739 static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 68740 { 68741 return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 68742 } 68743 68744 static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) 68745 { 68746 return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); 68747 } 68748 68749 static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) 68750 { 68751 return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); 68752 } 68753 68754 static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) 68755 { 68756 ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; 68757 MA_ASSERT(pDataBuffer != NULL); 68758 68759 ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); 68760 68761 /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ 68762 ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); 68763 68764 return MA_SUCCESS; 68765 } 68766 68767 static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = 68768 { 68769 ma_resource_manager_data_buffer_cb__read_pcm_frames, 68770 ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, 68771 ma_resource_manager_data_buffer_cb__get_data_format, 68772 ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, 68773 ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, 68774 ma_resource_manager_data_buffer_cb__set_looping, 68775 0 68776 }; 68777 68778 static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) 68779 { 68780 ma_result result = MA_SUCCESS; 68781 ma_resource_manager_data_buffer_node* pDataBufferNode; 68782 ma_data_source_config dataSourceConfig; 68783 ma_bool32 async; 68784 ma_uint32 flags; 68785 ma_resource_manager_pipeline_notifications notifications; 68786 68787 if (pDataBuffer == NULL) { 68788 if (pConfig != NULL && pConfig->pNotifications != NULL) { 68789 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); 68790 } 68791 68792 return MA_INVALID_ARGS; 68793 } 68794 68795 MA_ZERO_OBJECT(pDataBuffer); 68796 68797 if (pConfig == NULL) { 68798 return MA_INVALID_ARGS; 68799 } 68800 68801 if (pConfig->pNotifications != NULL) { 68802 notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ 68803 } else { 68804 MA_ZERO_OBJECT(¬ifications); 68805 } 68806 68807 /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ 68808 flags = pConfig->flags; 68809 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { 68810 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; 68811 } 68812 68813 async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; 68814 68815 /* 68816 Fences need to be acquired before doing anything. These must be acquired and released outside of 68817 the node to ensure there's no holes where ma_fence_wait() could prematurely return before the 68818 data buffer has completed initialization. 68819 68820 When loading asynchronously, the node acquisition routine below will acquire the fences on this 68821 thread and then release them on the async thread when the operation is complete. 68822 68823 These fences are always released at the "done" tag at the end of this function. They'll be 68824 acquired a second if loading asynchronously. This double acquisition system is just done to 68825 simplify code maintanence. 68826 */ 68827 ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); 68828 { 68829 /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ 68830 result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); 68831 if (result != MA_SUCCESS) { 68832 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 68833 goto done; 68834 } 68835 68836 dataSourceConfig = ma_data_source_config_init(); 68837 dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; 68838 68839 result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); 68840 if (result != MA_SUCCESS) { 68841 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); 68842 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 68843 goto done; 68844 } 68845 68846 pDataBuffer->pResourceManager = pResourceManager; 68847 pDataBuffer->pNode = pDataBufferNode; 68848 pDataBuffer->flags = flags; 68849 pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ 68850 68851 /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ 68852 if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { 68853 /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ 68854 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); 68855 ma_atomic_exchange_i32(&pDataBuffer->result, result); 68856 68857 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 68858 goto done; 68859 } else { 68860 /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ 68861 ma_job job; 68862 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ 68863 68864 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68865 ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); 68866 } 68867 68868 /* 68869 The status of the data buffer needs to be set to MA_BUSY before posting the job so that the 68870 worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other 68871 than MA_BUSY, it'll assume an error and fall through to an early exit. 68872 */ 68873 ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); 68874 68875 /* Acquire fences a second time. These will be released by the async thread. */ 68876 ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); 68877 68878 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); 68879 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); 68880 job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; 68881 job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; 68882 job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; 68883 job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; 68884 job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; 68885 job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; 68886 job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; 68887 job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; 68888 job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; 68889 job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; 68890 68891 /* If we need to wait for initialization to complete we can just process the job in place. */ 68892 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68893 result = ma_job_process(&job); 68894 } else { 68895 result = ma_resource_manager_post_job(pResourceManager, &job); 68896 } 68897 68898 if (result != MA_SUCCESS) { 68899 /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ 68900 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); 68901 ma_atomic_exchange_i32(&pDataBuffer->result, result); 68902 68903 /* Release the fences after the result has been set on the data buffer. */ 68904 ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); 68905 } else { 68906 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68907 ma_resource_manager_inline_notification_wait(&initNotification); 68908 68909 if (notifications.init.pNotification != NULL) { 68910 ma_async_notification_signal(notifications.init.pNotification); 68911 } 68912 68913 /* NOTE: Do not release the init fence here. It will have been done by the job. */ 68914 68915 /* Make sure we return an error if initialization failed on the async thread. */ 68916 result = ma_resource_manager_data_buffer_result(pDataBuffer); 68917 if (result == MA_BUSY) { 68918 result = MA_SUCCESS; 68919 } 68920 } 68921 } 68922 68923 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 68924 ma_resource_manager_inline_notification_uninit(&initNotification); 68925 } 68926 } 68927 68928 if (result != MA_SUCCESS) { 68929 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); 68930 goto done; 68931 } 68932 } 68933 done: 68934 if (result == MA_SUCCESS) { 68935 if (pConfig->initialSeekPointInPCMFrames > 0) { 68936 ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); 68937 } 68938 } 68939 68940 ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); 68941 68942 return result; 68943 } 68944 68945 MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) 68946 { 68947 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); 68948 } 68949 68950 MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) 68951 { 68952 ma_resource_manager_data_source_config config; 68953 68954 config = ma_resource_manager_data_source_config_init(); 68955 config.pFilePath = pFilePath; 68956 config.flags = flags; 68957 config.pNotifications = pNotifications; 68958 68959 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); 68960 } 68961 68962 MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) 68963 { 68964 ma_resource_manager_data_source_config config; 68965 68966 config = ma_resource_manager_data_source_config_init(); 68967 config.pFilePathW = pFilePath; 68968 config.flags = flags; 68969 config.pNotifications = pNotifications; 68970 68971 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); 68972 } 68973 68974 MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) 68975 { 68976 ma_resource_manager_data_source_config config; 68977 68978 if (pExistingDataBuffer == NULL) { 68979 return MA_INVALID_ARGS; 68980 } 68981 68982 MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ 68983 68984 config = ma_resource_manager_data_source_config_init(); 68985 config.flags = pExistingDataBuffer->flags; 68986 68987 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); 68988 } 68989 68990 static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) 68991 { 68992 MA_ASSERT(pDataBuffer != NULL); 68993 68994 /* The connector should be uninitialized first. */ 68995 ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); 68996 68997 /* With the connector uninitialized we can unacquire the node. */ 68998 ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); 68999 69000 /* The base data source needs to be uninitialized as well. */ 69001 ma_data_source_uninit(&pDataBuffer->ds); 69002 69003 return MA_SUCCESS; 69004 } 69005 69006 MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) 69007 { 69008 ma_result result; 69009 69010 if (pDataBuffer == NULL) { 69011 return MA_INVALID_ARGS; 69012 } 69013 69014 if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { 69015 /* The data buffer can be deleted synchronously. */ 69016 return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); 69017 } else { 69018 /* 69019 The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will 69020 be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event 69021 to get processed before returning. 69022 */ 69023 ma_resource_manager_inline_notification notification; 69024 ma_job job; 69025 69026 /* 69027 We need to mark the node as unavailable so we don't try reading from it anymore, but also to 69028 let the loading thread know that it needs to abort it's loading procedure. 69029 */ 69030 ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); 69031 69032 result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); 69033 if (result != MA_SUCCESS) { 69034 return result; /* Failed to create the notification. This should rarely, if ever, happen. */ 69035 } 69036 69037 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); 69038 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); 69039 job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; 69040 job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; 69041 job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; 69042 69043 result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); 69044 if (result != MA_SUCCESS) { 69045 ma_resource_manager_inline_notification_uninit(¬ification); 69046 return result; 69047 } 69048 69049 ma_resource_manager_inline_notification_wait_and_uninit(¬ification); 69050 } 69051 69052 return result; 69053 } 69054 69055 MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 69056 { 69057 ma_result result = MA_SUCCESS; 69058 ma_uint64 framesRead = 0; 69059 ma_bool32 isDecodedBufferBusy = MA_FALSE; 69060 69061 /* Safety. */ 69062 if (pFramesRead != NULL) { 69063 *pFramesRead = 0; 69064 } 69065 69066 if (frameCount == 0) { 69067 return MA_INVALID_ARGS; 69068 } 69069 69070 /* 69071 We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after 69072 it's been uninitialized or is in the process of uninitializing. 69073 */ 69074 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); 69075 69076 /* If the node is not initialized we need to abort with a busy code. */ 69077 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { 69078 return MA_BUSY; /* Still loading. */ 69079 } 69080 69081 /* 69082 If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's 69083 a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If 69084 this happens, we need to keep the seek scheduled and return MA_BUSY. 69085 */ 69086 if (pDataBuffer->seekToCursorOnNextRead) { 69087 pDataBuffer->seekToCursorOnNextRead = MA_FALSE; 69088 69089 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); 69090 if (result != MA_SUCCESS) { 69091 if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { 69092 pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ 69093 return MA_BUSY; 69094 } 69095 69096 return result; 69097 } 69098 } 69099 69100 /* 69101 For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot 69102 exceed this amount. We'll read as much as we can, and then return MA_BUSY. 69103 */ 69104 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { 69105 ma_uint64 availableFrames; 69106 69107 isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); 69108 69109 if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { 69110 /* Don't try reading more than the available frame count. */ 69111 if (frameCount > availableFrames) { 69112 frameCount = availableFrames; 69113 69114 /* 69115 If there's no frames available we want to set the status to MA_AT_END. The logic below 69116 will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this 69117 is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count 69118 is 0 because that'll result in a situation where it's possible MA_AT_END won't get 69119 returned. 69120 */ 69121 if (frameCount == 0) { 69122 result = MA_AT_END; 69123 } 69124 } else { 69125 isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ 69126 } 69127 } 69128 } 69129 69130 /* Don't attempt to read anything if we've got no frames available. */ 69131 if (frameCount > 0) { 69132 result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); 69133 } 69134 69135 /* 69136 If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound 69137 as at the end and terminate decoding. 69138 */ 69139 if (result == MA_AT_END) { 69140 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { 69141 result = MA_BUSY; 69142 } 69143 } 69144 69145 if (isDecodedBufferBusy) { 69146 result = MA_BUSY; 69147 } 69148 69149 if (pFramesRead != NULL) { 69150 *pFramesRead = framesRead; 69151 } 69152 69153 if (result == MA_SUCCESS && framesRead == 0) { 69154 result = MA_AT_END; 69155 } 69156 69157 return result; 69158 } 69159 69160 MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) 69161 { 69162 ma_result result; 69163 69164 /* We cannot be using the data source after it's been uninitialized. */ 69165 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); 69166 69167 /* If we haven't yet got a connector we need to abort. */ 69168 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { 69169 pDataBuffer->seekTargetInPCMFrames = frameIndex; 69170 pDataBuffer->seekToCursorOnNextRead = MA_TRUE; 69171 return MA_BUSY; /* Still loading. */ 69172 } 69173 69174 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); 69175 if (result != MA_SUCCESS) { 69176 return result; 69177 } 69178 69179 pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ 69180 pDataBuffer->seekToCursorOnNextRead = MA_FALSE; 69181 69182 return MA_SUCCESS; 69183 } 69184 69185 MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 69186 { 69187 /* We cannot be using the data source after it's been uninitialized. */ 69188 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); 69189 69190 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) 69191 { 69192 case ma_resource_manager_data_supply_type_encoded: 69193 { 69194 return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 69195 }; 69196 69197 case ma_resource_manager_data_supply_type_decoded: 69198 { 69199 *pFormat = pDataBuffer->pNode->data.backend.decoded.format; 69200 *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; 69201 *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; 69202 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); 69203 return MA_SUCCESS; 69204 }; 69205 69206 case ma_resource_manager_data_supply_type_decoded_paged: 69207 { 69208 *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; 69209 *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; 69210 *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; 69211 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); 69212 return MA_SUCCESS; 69213 }; 69214 69215 case ma_resource_manager_data_supply_type_unknown: 69216 { 69217 return MA_BUSY; /* Still loading. */ 69218 }; 69219 69220 default: 69221 { 69222 /* Unknown supply type. Should never hit this. */ 69223 return MA_INVALID_ARGS; 69224 } 69225 } 69226 } 69227 69228 MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) 69229 { 69230 /* We cannot be using the data source after it's been uninitialized. */ 69231 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); 69232 69233 if (pDataBuffer == NULL || pCursor == NULL) { 69234 return MA_INVALID_ARGS; 69235 } 69236 69237 *pCursor = 0; 69238 69239 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) 69240 { 69241 case ma_resource_manager_data_supply_type_encoded: 69242 { 69243 return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); 69244 }; 69245 69246 case ma_resource_manager_data_supply_type_decoded: 69247 { 69248 return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); 69249 }; 69250 69251 case ma_resource_manager_data_supply_type_decoded_paged: 69252 { 69253 return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); 69254 }; 69255 69256 case ma_resource_manager_data_supply_type_unknown: 69257 { 69258 return MA_BUSY; 69259 }; 69260 69261 default: 69262 { 69263 return MA_INVALID_ARGS; 69264 } 69265 } 69266 } 69267 69268 MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) 69269 { 69270 /* We cannot be using the data source after it's been uninitialized. */ 69271 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); 69272 69273 if (pDataBuffer == NULL || pLength == NULL) { 69274 return MA_INVALID_ARGS; 69275 } 69276 69277 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { 69278 return MA_BUSY; /* Still loading. */ 69279 } 69280 69281 return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); 69282 } 69283 69284 MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) 69285 { 69286 if (pDataBuffer == NULL) { 69287 return MA_INVALID_ARGS; 69288 } 69289 69290 return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ 69291 } 69292 69293 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) 69294 { 69295 return ma_data_source_set_looping(pDataBuffer, isLooping); 69296 } 69297 69298 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) 69299 { 69300 return ma_data_source_is_looping(pDataBuffer); 69301 } 69302 69303 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) 69304 { 69305 if (pAvailableFrames == NULL) { 69306 return MA_INVALID_ARGS; 69307 } 69308 69309 *pAvailableFrames = 0; 69310 69311 if (pDataBuffer == NULL) { 69312 return MA_INVALID_ARGS; 69313 } 69314 69315 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { 69316 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { 69317 return MA_BUSY; 69318 } else { 69319 return MA_INVALID_OPERATION; /* No connector. */ 69320 } 69321 } 69322 69323 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) 69324 { 69325 case ma_resource_manager_data_supply_type_encoded: 69326 { 69327 return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); 69328 }; 69329 69330 case ma_resource_manager_data_supply_type_decoded: 69331 { 69332 return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); 69333 }; 69334 69335 case ma_resource_manager_data_supply_type_decoded_paged: 69336 { 69337 ma_uint64 cursor; 69338 ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); 69339 69340 if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { 69341 *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; 69342 } else { 69343 *pAvailableFrames = 0; 69344 } 69345 69346 return MA_SUCCESS; 69347 }; 69348 69349 case ma_resource_manager_data_supply_type_unknown: 69350 default: 69351 { 69352 /* Unknown supply type. Should never hit this. */ 69353 return MA_INVALID_ARGS; 69354 } 69355 } 69356 } 69357 69358 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) 69359 { 69360 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); 69361 } 69362 69363 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) 69364 { 69365 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); 69366 } 69367 69368 69369 static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) 69370 { 69371 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); 69372 } 69373 69374 static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 69375 { 69376 ma_resource_manager_data_supply data; 69377 data.type = ma_resource_manager_data_supply_type_decoded; 69378 data.backend.decoded.pData = pData; 69379 data.backend.decoded.totalFrameCount = frameCount; 69380 data.backend.decoded.format = format; 69381 data.backend.decoded.channels = channels; 69382 data.backend.decoded.sampleRate = sampleRate; 69383 69384 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); 69385 } 69386 69387 MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 69388 { 69389 return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); 69390 } 69391 69392 MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 69393 { 69394 return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); 69395 } 69396 69397 69398 static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) 69399 { 69400 ma_resource_manager_data_supply data; 69401 data.type = ma_resource_manager_data_supply_type_encoded; 69402 data.backend.encoded.pData = pData; 69403 data.backend.encoded.sizeInBytes = sizeInBytes; 69404 69405 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); 69406 } 69407 69408 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) 69409 { 69410 return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); 69411 } 69412 69413 MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) 69414 { 69415 return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); 69416 } 69417 69418 69419 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) 69420 { 69421 return ma_resource_manager_unregister_data(pResourceManager, pFilePath); 69422 } 69423 69424 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) 69425 { 69426 return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); 69427 } 69428 69429 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) 69430 { 69431 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); 69432 } 69433 69434 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) 69435 { 69436 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); 69437 } 69438 69439 69440 static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) 69441 { 69442 MA_ASSERT(pDataStream != NULL); 69443 return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); 69444 } 69445 69446 static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) 69447 { 69448 MA_ASSERT(pDataStream != NULL); 69449 return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); 69450 } 69451 69452 static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) 69453 { 69454 MA_ASSERT(pDataStream != NULL); 69455 return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); 69456 } 69457 69458 69459 static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 69460 { 69461 return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); 69462 } 69463 69464 static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) 69465 { 69466 return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); 69467 } 69468 69469 static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 69470 { 69471 return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 69472 } 69473 69474 static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) 69475 { 69476 return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); 69477 } 69478 69479 static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) 69480 { 69481 return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); 69482 } 69483 69484 static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) 69485 { 69486 ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; 69487 MA_ASSERT(pDataStream != NULL); 69488 69489 ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); 69490 69491 return MA_SUCCESS; 69492 } 69493 69494 static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = 69495 { 69496 ma_resource_manager_data_stream_cb__read_pcm_frames, 69497 ma_resource_manager_data_stream_cb__seek_to_pcm_frame, 69498 ma_resource_manager_data_stream_cb__get_data_format, 69499 ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, 69500 ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, 69501 ma_resource_manager_data_stream_cb__set_looping, 69502 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ 69503 }; 69504 69505 static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) 69506 { 69507 /* Loop if possible. */ 69508 if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { 69509 absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; 69510 } 69511 69512 ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); 69513 } 69514 69515 MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) 69516 { 69517 ma_result result; 69518 ma_data_source_config dataSourceConfig; 69519 char* pFilePathCopy = NULL; 69520 wchar_t* pFilePathWCopy = NULL; 69521 ma_job job; 69522 ma_bool32 waitBeforeReturning = MA_FALSE; 69523 ma_resource_manager_inline_notification waitNotification; 69524 ma_resource_manager_pipeline_notifications notifications; 69525 69526 if (pDataStream == NULL) { 69527 if (pConfig != NULL && pConfig->pNotifications != NULL) { 69528 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); 69529 } 69530 69531 return MA_INVALID_ARGS; 69532 } 69533 69534 MA_ZERO_OBJECT(pDataStream); 69535 69536 if (pConfig == NULL) { 69537 return MA_INVALID_ARGS; 69538 } 69539 69540 if (pConfig->pNotifications != NULL) { 69541 notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ 69542 } else { 69543 MA_ZERO_OBJECT(¬ifications); 69544 } 69545 69546 dataSourceConfig = ma_data_source_config_init(); 69547 dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; 69548 69549 result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); 69550 if (result != MA_SUCCESS) { 69551 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 69552 return result; 69553 } 69554 69555 pDataStream->pResourceManager = pResourceManager; 69556 pDataStream->flags = pConfig->flags; 69557 pDataStream->result = MA_BUSY; 69558 69559 ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); 69560 ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); 69561 ma_data_source_set_looping(pDataStream, pConfig->isLooping); 69562 69563 if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { 69564 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 69565 return MA_INVALID_ARGS; 69566 } 69567 69568 /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ 69569 69570 /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ 69571 if (pConfig->pFilePath != NULL) { 69572 pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); 69573 } else { 69574 pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); 69575 } 69576 69577 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { 69578 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 69579 return MA_OUT_OF_MEMORY; 69580 } 69581 69582 /* 69583 We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we 69584 can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. 69585 */ 69586 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { 69587 waitBeforeReturning = MA_TRUE; 69588 ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); 69589 } 69590 69591 ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); 69592 69593 /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ 69594 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); 69595 69596 /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ 69597 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); 69598 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); 69599 job.data.resourceManager.loadDataStream.pDataStream = pDataStream; 69600 job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; 69601 job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; 69602 job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; 69603 job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; 69604 job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; 69605 result = ma_resource_manager_post_job(pResourceManager, &job); 69606 if (result != MA_SUCCESS) { 69607 ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); 69608 ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); 69609 69610 if (waitBeforeReturning) { 69611 ma_resource_manager_inline_notification_uninit(&waitNotification); 69612 } 69613 69614 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); 69615 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); 69616 return result; 69617 } 69618 69619 /* Wait if needed. */ 69620 if (waitBeforeReturning) { 69621 ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); 69622 69623 if (notifications.init.pNotification != NULL) { 69624 ma_async_notification_signal(notifications.init.pNotification); 69625 } 69626 69627 /* 69628 If there was an error during initialization make sure we return that result here. We don't want to do this 69629 if we're not waiting because it will most likely be in a busy state. 69630 */ 69631 if (pDataStream->result != MA_SUCCESS) { 69632 return pDataStream->result; 69633 } 69634 69635 /* NOTE: Do not release pInitFence here. That will be done by the job. */ 69636 } 69637 69638 return MA_SUCCESS; 69639 } 69640 69641 MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) 69642 { 69643 ma_resource_manager_data_source_config config; 69644 69645 config = ma_resource_manager_data_source_config_init(); 69646 config.pFilePath = pFilePath; 69647 config.flags = flags; 69648 config.pNotifications = pNotifications; 69649 69650 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); 69651 } 69652 69653 MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) 69654 { 69655 ma_resource_manager_data_source_config config; 69656 69657 config = ma_resource_manager_data_source_config_init(); 69658 config.pFilePathW = pFilePath; 69659 config.flags = flags; 69660 config.pNotifications = pNotifications; 69661 69662 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); 69663 } 69664 69665 MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) 69666 { 69667 ma_resource_manager_inline_notification freeEvent; 69668 ma_job job; 69669 69670 if (pDataStream == NULL) { 69671 return MA_INVALID_ARGS; 69672 } 69673 69674 /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ 69675 ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); 69676 69677 /* 69678 We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need 69679 to wait for it to complete before returning which means we need an event. 69680 */ 69681 ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); 69682 69683 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); 69684 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); 69685 job.data.resourceManager.freeDataStream.pDataStream = pDataStream; 69686 job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; 69687 job.data.resourceManager.freeDataStream.pDoneFence = NULL; 69688 ma_resource_manager_post_job(pDataStream->pResourceManager, &job); 69689 69690 /* We need to wait for the job to finish processing before we return. */ 69691 ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); 69692 69693 return MA_SUCCESS; 69694 } 69695 69696 69697 static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) 69698 { 69699 MA_ASSERT(pDataStream != NULL); 69700 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); 69701 69702 return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); 69703 } 69704 69705 static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) 69706 { 69707 MA_ASSERT(pDataStream != NULL); 69708 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); 69709 MA_ASSERT(pageIndex == 0 || pageIndex == 1); 69710 69711 return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); 69712 } 69713 69714 static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) 69715 { 69716 ma_result result = MA_SUCCESS; 69717 ma_uint64 pageSizeInFrames; 69718 ma_uint64 totalFramesReadForThisPage = 0; 69719 void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); 69720 69721 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); 69722 69723 /* The decoder needs to inherit the stream's looping and range state. */ 69724 { 69725 ma_uint64 rangeBeg; 69726 ma_uint64 rangeEnd; 69727 ma_uint64 loopPointBeg; 69728 ma_uint64 loopPointEnd; 69729 69730 ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); 69731 69732 ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); 69733 ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); 69734 69735 ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); 69736 ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); 69737 } 69738 69739 /* Just read straight from the decoder. It will deal with ranges and looping for us. */ 69740 result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); 69741 if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { 69742 ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); 69743 } 69744 69745 ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); 69746 ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); 69747 } 69748 69749 static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) 69750 { 69751 ma_uint32 iPage; 69752 69753 MA_ASSERT(pDataStream != NULL); 69754 69755 for (iPage = 0; iPage < 2; iPage += 1) { 69756 ma_resource_manager_data_stream_fill_page(pDataStream, iPage); 69757 } 69758 } 69759 69760 69761 static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) 69762 { 69763 ma_uint64 framesAvailable; 69764 ma_uint64 frameCount = 0; 69765 69766 /* We cannot be using the data source after it's been uninitialized. */ 69767 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); 69768 69769 if (pFrameCount != NULL) { 69770 frameCount = *pFrameCount; 69771 *pFrameCount = 0; 69772 } 69773 if (ppFramesOut != NULL) { 69774 *ppFramesOut = NULL; 69775 } 69776 69777 if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { 69778 return MA_INVALID_ARGS; 69779 } 69780 69781 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { 69782 return MA_INVALID_OPERATION; 69783 } 69784 69785 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ 69786 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { 69787 return MA_BUSY; 69788 } 69789 69790 /* If the page we're on is invalid it means we've caught up to the job thread. */ 69791 if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { 69792 framesAvailable = 0; 69793 } else { 69794 /* 69795 The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is 69796 that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. 69797 */ 69798 ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); 69799 MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); 69800 69801 framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; 69802 } 69803 69804 /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ 69805 if (framesAvailable == 0) { 69806 if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { 69807 return MA_AT_END; 69808 } else { 69809 return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ 69810 } 69811 } 69812 69813 MA_ASSERT(framesAvailable > 0); 69814 69815 if (frameCount > framesAvailable) { 69816 frameCount = framesAvailable; 69817 } 69818 69819 *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); 69820 *pFrameCount = frameCount; 69821 69822 return MA_SUCCESS; 69823 } 69824 69825 static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) 69826 { 69827 ma_uint32 newRelativeCursor; 69828 ma_uint32 pageSizeInFrames; 69829 ma_job job; 69830 69831 /* We cannot be using the data source after it's been uninitialized. */ 69832 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); 69833 69834 if (pDataStream == NULL) { 69835 return MA_INVALID_ARGS; 69836 } 69837 69838 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { 69839 return MA_INVALID_OPERATION; 69840 } 69841 69842 /* The frame count should always fit inside a 32-bit integer. */ 69843 if (frameCount > 0xFFFFFFFF) { 69844 return MA_INVALID_ARGS; 69845 } 69846 69847 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); 69848 69849 /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ 69850 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); 69851 69852 /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ 69853 newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; 69854 69855 /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ 69856 if (newRelativeCursor >= pageSizeInFrames) { 69857 newRelativeCursor -= pageSizeInFrames; 69858 69859 /* Here is where we post the job start decoding. */ 69860 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); 69861 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); 69862 job.data.resourceManager.pageDataStream.pDataStream = pDataStream; 69863 job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; 69864 69865 /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ 69866 ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); 69867 69868 /* Before posting the job we need to make sure we set some state. */ 69869 pDataStream->relativeCursor = newRelativeCursor; 69870 pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; 69871 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); 69872 } else { 69873 /* We haven't moved into a new page so we can just move the cursor forward. */ 69874 pDataStream->relativeCursor = newRelativeCursor; 69875 return MA_SUCCESS; 69876 } 69877 } 69878 69879 69880 MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 69881 { 69882 ma_result result = MA_SUCCESS; 69883 ma_uint64 totalFramesProcessed; 69884 ma_format format; 69885 ma_uint32 channels; 69886 69887 /* Safety. */ 69888 if (pFramesRead != NULL) { 69889 *pFramesRead = 0; 69890 } 69891 69892 if (frameCount == 0) { 69893 return MA_INVALID_ARGS; 69894 } 69895 69896 /* We cannot be using the data source after it's been uninitialized. */ 69897 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); 69898 69899 if (pDataStream == NULL) { 69900 return MA_INVALID_ARGS; 69901 } 69902 69903 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { 69904 return MA_INVALID_OPERATION; 69905 } 69906 69907 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ 69908 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { 69909 return MA_BUSY; 69910 } 69911 69912 ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); 69913 69914 /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ 69915 totalFramesProcessed = 0; 69916 while (totalFramesProcessed < frameCount) { 69917 void* pMappedFrames; 69918 ma_uint64 mappedFrameCount; 69919 69920 mappedFrameCount = frameCount - totalFramesProcessed; 69921 result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); 69922 if (result != MA_SUCCESS) { 69923 break; 69924 } 69925 69926 /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ 69927 if (pFramesOut != NULL) { 69928 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); 69929 } 69930 69931 totalFramesProcessed += mappedFrameCount; 69932 69933 result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); 69934 if (result != MA_SUCCESS) { 69935 break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ 69936 } 69937 } 69938 69939 if (pFramesRead != NULL) { 69940 *pFramesRead = totalFramesProcessed; 69941 } 69942 69943 if (result == MA_SUCCESS && totalFramesProcessed == 0) { 69944 result = MA_AT_END; 69945 } 69946 69947 return result; 69948 } 69949 69950 MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) 69951 { 69952 ma_job job; 69953 ma_result streamResult; 69954 69955 streamResult = ma_resource_manager_data_stream_result(pDataStream); 69956 69957 /* We cannot be using the data source after it's been uninitialized. */ 69958 MA_ASSERT(streamResult != MA_UNAVAILABLE); 69959 69960 if (pDataStream == NULL) { 69961 return MA_INVALID_ARGS; 69962 } 69963 69964 if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { 69965 return MA_INVALID_OPERATION; 69966 } 69967 69968 /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ 69969 if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { 69970 if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { 69971 return MA_SUCCESS; 69972 } 69973 } 69974 69975 69976 /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ 69977 ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); 69978 69979 /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ 69980 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); 69981 69982 /* 69983 We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public 69984 API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of 69985 the first page. 69986 */ 69987 pDataStream->relativeCursor = 0; 69988 pDataStream->currentPageIndex = 0; 69989 ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); 69990 ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); 69991 69992 /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ 69993 ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); 69994 69995 /* 69996 The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages 69997 are invalid and any content contained within them will be discarded and replaced with newly decoded data. 69998 */ 69999 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); 70000 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); 70001 job.data.resourceManager.seekDataStream.pDataStream = pDataStream; 70002 job.data.resourceManager.seekDataStream.frameIndex = frameIndex; 70003 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); 70004 } 70005 70006 MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 70007 { 70008 /* We cannot be using the data source after it's been uninitialized. */ 70009 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); 70010 70011 if (pFormat != NULL) { 70012 *pFormat = ma_format_unknown; 70013 } 70014 70015 if (pChannels != NULL) { 70016 *pChannels = 0; 70017 } 70018 70019 if (pSampleRate != NULL) { 70020 *pSampleRate = 0; 70021 } 70022 70023 if (pChannelMap != NULL) { 70024 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); 70025 } 70026 70027 if (pDataStream == NULL) { 70028 return MA_INVALID_ARGS; 70029 } 70030 70031 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { 70032 return MA_INVALID_OPERATION; 70033 } 70034 70035 /* 70036 We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function 70037 such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. 70038 */ 70039 return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 70040 } 70041 70042 MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) 70043 { 70044 ma_result result; 70045 70046 if (pCursor == NULL) { 70047 return MA_INVALID_ARGS; 70048 } 70049 70050 *pCursor = 0; 70051 70052 /* We cannot be using the data source after it's been uninitialized. */ 70053 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); 70054 70055 if (pDataStream == NULL) { 70056 return MA_INVALID_ARGS; 70057 } 70058 70059 /* 70060 If the stream is in an erroneous state we need to return an invalid operation. We can allow 70061 this to be called when the data stream is in a busy state because the caller may have asked 70062 for an initial seek position and it's convenient to return that as the cursor position. 70063 */ 70064 result = ma_resource_manager_data_stream_result(pDataStream); 70065 if (result != MA_SUCCESS && result != MA_BUSY) { 70066 return MA_INVALID_OPERATION; 70067 } 70068 70069 *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); 70070 70071 return MA_SUCCESS; 70072 } 70073 70074 MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) 70075 { 70076 ma_result streamResult; 70077 70078 if (pLength == NULL) { 70079 return MA_INVALID_ARGS; 70080 } 70081 70082 *pLength = 0; 70083 70084 streamResult = ma_resource_manager_data_stream_result(pDataStream); 70085 70086 /* We cannot be using the data source after it's been uninitialized. */ 70087 MA_ASSERT(streamResult != MA_UNAVAILABLE); 70088 70089 if (pDataStream == NULL) { 70090 return MA_INVALID_ARGS; 70091 } 70092 70093 if (streamResult != MA_SUCCESS) { 70094 return streamResult; 70095 } 70096 70097 /* 70098 We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we 70099 calculated when we initialized it on the job thread. 70100 */ 70101 *pLength = pDataStream->totalLengthInPCMFrames; 70102 if (*pLength == 0) { 70103 return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ 70104 } 70105 70106 return MA_SUCCESS; 70107 } 70108 70109 MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) 70110 { 70111 if (pDataStream == NULL) { 70112 return MA_INVALID_ARGS; 70113 } 70114 70115 return (ma_result)ma_atomic_load_i32(&pDataStream->result); 70116 } 70117 70118 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) 70119 { 70120 return ma_data_source_set_looping(pDataStream, isLooping); 70121 } 70122 70123 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) 70124 { 70125 if (pDataStream == NULL) { 70126 return MA_FALSE; 70127 } 70128 70129 return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ 70130 } 70131 70132 MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) 70133 { 70134 ma_uint32 pageIndex0; 70135 ma_uint32 pageIndex1; 70136 ma_uint32 relativeCursor; 70137 ma_uint64 availableFrames; 70138 70139 if (pAvailableFrames == NULL) { 70140 return MA_INVALID_ARGS; 70141 } 70142 70143 *pAvailableFrames = 0; 70144 70145 if (pDataStream == NULL) { 70146 return MA_INVALID_ARGS; 70147 } 70148 70149 pageIndex0 = pDataStream->currentPageIndex; 70150 pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; 70151 relativeCursor = pDataStream->relativeCursor; 70152 70153 availableFrames = 0; 70154 if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { 70155 availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; 70156 if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { 70157 availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); 70158 } 70159 } 70160 70161 *pAvailableFrames = availableFrames; 70162 return MA_SUCCESS; 70163 } 70164 70165 70166 static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) 70167 { 70168 if (pDataSource == NULL) { 70169 return MA_INVALID_ARGS; 70170 } 70171 70172 MA_ZERO_OBJECT(pDataSource); 70173 70174 if (pConfig == NULL) { 70175 return MA_INVALID_ARGS; 70176 } 70177 70178 if (pResourceManager == NULL) { 70179 return MA_INVALID_ARGS; 70180 } 70181 70182 pDataSource->flags = pConfig->flags; 70183 70184 return MA_SUCCESS; 70185 } 70186 70187 MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) 70188 { 70189 ma_result result; 70190 70191 result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); 70192 if (result != MA_SUCCESS) { 70193 return result; 70194 } 70195 70196 /* The data source itself is just a data stream or a data buffer. */ 70197 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70198 return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); 70199 } else { 70200 return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); 70201 } 70202 } 70203 70204 MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) 70205 { 70206 ma_resource_manager_data_source_config config; 70207 70208 config = ma_resource_manager_data_source_config_init(); 70209 config.pFilePath = pName; 70210 config.flags = flags; 70211 config.pNotifications = pNotifications; 70212 70213 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); 70214 } 70215 70216 MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) 70217 { 70218 ma_resource_manager_data_source_config config; 70219 70220 config = ma_resource_manager_data_source_config_init(); 70221 config.pFilePathW = pName; 70222 config.flags = flags; 70223 config.pNotifications = pNotifications; 70224 70225 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); 70226 } 70227 70228 MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) 70229 { 70230 ma_result result; 70231 ma_resource_manager_data_source_config config; 70232 70233 if (pExistingDataSource == NULL) { 70234 return MA_INVALID_ARGS; 70235 } 70236 70237 config = ma_resource_manager_data_source_config_init(); 70238 config.flags = pExistingDataSource->flags; 70239 70240 result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); 70241 if (result != MA_SUCCESS) { 70242 return result; 70243 } 70244 70245 /* Copying can only be done from data buffers. Streams cannot be copied. */ 70246 if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70247 return MA_INVALID_OPERATION; 70248 } 70249 70250 return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); 70251 } 70252 70253 MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) 70254 { 70255 if (pDataSource == NULL) { 70256 return MA_INVALID_ARGS; 70257 } 70258 70259 /* All we need to is uninitialize the underlying data buffer or data stream. */ 70260 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70261 return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); 70262 } else { 70263 return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); 70264 } 70265 } 70266 70267 MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 70268 { 70269 /* Safety. */ 70270 if (pFramesRead != NULL) { 70271 *pFramesRead = 0; 70272 } 70273 70274 if (pDataSource == NULL) { 70275 return MA_INVALID_ARGS; 70276 } 70277 70278 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70279 return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); 70280 } else { 70281 return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); 70282 } 70283 } 70284 70285 MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) 70286 { 70287 if (pDataSource == NULL) { 70288 return MA_INVALID_ARGS; 70289 } 70290 70291 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70292 return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); 70293 } else { 70294 return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); 70295 } 70296 } 70297 70298 MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) 70299 { 70300 if (pDataSource == NULL) { 70301 return MA_INVALID_ARGS; 70302 } 70303 70304 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70305 return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); 70306 } else { 70307 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ 70308 } 70309 } 70310 70311 MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) 70312 { 70313 if (pDataSource == NULL) { 70314 return MA_INVALID_ARGS; 70315 } 70316 70317 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70318 return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); 70319 } else { 70320 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ 70321 } 70322 } 70323 70324 MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 70325 { 70326 if (pDataSource == NULL) { 70327 return MA_INVALID_ARGS; 70328 } 70329 70330 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70331 return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 70332 } else { 70333 return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 70334 } 70335 } 70336 70337 MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) 70338 { 70339 if (pDataSource == NULL) { 70340 return MA_INVALID_ARGS; 70341 } 70342 70343 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70344 return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); 70345 } else { 70346 return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); 70347 } 70348 } 70349 70350 MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) 70351 { 70352 if (pDataSource == NULL) { 70353 return MA_INVALID_ARGS; 70354 } 70355 70356 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70357 return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); 70358 } else { 70359 return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); 70360 } 70361 } 70362 70363 MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) 70364 { 70365 if (pDataSource == NULL) { 70366 return MA_INVALID_ARGS; 70367 } 70368 70369 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70370 return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); 70371 } else { 70372 return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); 70373 } 70374 } 70375 70376 MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) 70377 { 70378 if (pDataSource == NULL) { 70379 return MA_INVALID_ARGS; 70380 } 70381 70382 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70383 return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); 70384 } else { 70385 return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); 70386 } 70387 } 70388 70389 MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) 70390 { 70391 if (pDataSource == NULL) { 70392 return MA_FALSE; 70393 } 70394 70395 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70396 return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); 70397 } else { 70398 return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); 70399 } 70400 } 70401 70402 MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) 70403 { 70404 if (pAvailableFrames == NULL) { 70405 return MA_INVALID_ARGS; 70406 } 70407 70408 *pAvailableFrames = 0; 70409 70410 if (pDataSource == NULL) { 70411 return MA_INVALID_ARGS; 70412 } 70413 70414 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { 70415 return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); 70416 } else { 70417 return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); 70418 } 70419 } 70420 70421 70422 MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) 70423 { 70424 if (pResourceManager == NULL) { 70425 return MA_INVALID_ARGS; 70426 } 70427 70428 return ma_job_queue_post(&pResourceManager->jobQueue, pJob); 70429 } 70430 70431 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) 70432 { 70433 ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); 70434 return ma_resource_manager_post_job(pResourceManager, &job); 70435 } 70436 70437 MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) 70438 { 70439 if (pResourceManager == NULL) { 70440 return MA_INVALID_ARGS; 70441 } 70442 70443 return ma_job_queue_next(&pResourceManager->jobQueue, pJob); 70444 } 70445 70446 70447 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) 70448 { 70449 ma_result result = MA_SUCCESS; 70450 ma_resource_manager* pResourceManager; 70451 ma_resource_manager_data_buffer_node* pDataBufferNode; 70452 70453 MA_ASSERT(pJob != NULL); 70454 70455 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; 70456 MA_ASSERT(pResourceManager != NULL); 70457 70458 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; 70459 MA_ASSERT(pDataBufferNode != NULL); 70460 MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ 70461 70462 /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ 70463 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { 70464 return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ 70465 } 70466 70467 /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ 70468 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { 70469 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ 70470 goto done; 70471 } 70472 70473 /* 70474 We're ready to start loading. Essentially what we're doing here is initializing the data supply 70475 of the node. Once this is complete, data buffers can have their connectors initialized which 70476 will allow then to have audio data read from them. 70477 70478 Note that when the data supply type has been moved away from "unknown", that is when other threads 70479 will determine that the node is available for data delivery and the data buffer connectors can be 70480 initialized. Therefore, it's important that it is set after the data supply has been initialized. 70481 */ 70482 if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { 70483 /* 70484 Decoding. This is the complex case because we're not going to be doing the entire decoding 70485 process here. Instead it's going to be split of multiple jobs and loaded in pages. The 70486 reason for this is to evenly distribute decoding time across multiple sounds, rather than 70487 having one huge sound hog all the available processing resources. 70488 70489 The first thing we do is initialize a decoder. This is allocated on the heap and is passed 70490 around to the paging jobs. When the last paging job has completed it's processing, it'll 70491 free the decoder for us. 70492 70493 This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job 70494 which is where the actual decoding work will be done. However, once this job is complete, 70495 the node will be in a state where data buffer connectors can be initialized. 70496 */ 70497 ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ 70498 ma_job pageDataBufferNodeJob; 70499 70500 /* Allocate the decoder by initializing a decoded data supply. */ 70501 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); 70502 70503 /* 70504 Don't ever propagate an MA_BUSY result code or else the resource manager will think the 70505 node is just busy decoding rather than in an error state. This should never happen, but 70506 including this logic for safety just in case. 70507 */ 70508 if (result == MA_BUSY) { 70509 result = MA_ERROR; 70510 } 70511 70512 if (result != MA_SUCCESS) { 70513 if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { 70514 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); 70515 } else { 70516 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) 70517 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); 70518 #endif 70519 } 70520 70521 goto done; 70522 } 70523 70524 /* 70525 At this point the node's data supply is initialized and other threads can start initializing 70526 their data buffer connectors. However, no data will actually be available until we start to 70527 actually decode it. To do this, we need to post a paging job which is where the decoding 70528 work is done. 70529 70530 Note that if an error occurred at an earlier point, this section will have been skipped. 70531 */ 70532 pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); 70533 pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); 70534 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; 70535 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; 70536 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; 70537 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; 70538 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; 70539 70540 /* The job has been set up so it can now be posted. */ 70541 result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); 70542 70543 /* 70544 When we get here, we want to make sure the result code is set to MA_BUSY. The reason for 70545 this is that the result will be copied over to the node's internal result variable. In 70546 this case, since the decoding is still in-progress, we need to make sure the result code 70547 is set to MA_BUSY. 70548 */ 70549 if (result != MA_SUCCESS) { 70550 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); 70551 ma_decoder_uninit(pDecoder); 70552 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); 70553 } else { 70554 result = MA_BUSY; 70555 } 70556 } else { 70557 /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ 70558 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); 70559 } 70560 70561 70562 done: 70563 /* File paths are no longer needed. */ 70564 ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); 70565 ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); 70566 70567 /* 70568 We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads 70569 are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY 70570 because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then 70571 immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any 70572 other error code would cause the buffer to look like it's in a state that it's not. 70573 */ 70574 ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); 70575 70576 /* At this point initialization is complete and we can signal the notification if any. */ 70577 if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { 70578 ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); 70579 } 70580 if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { 70581 ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); 70582 } 70583 70584 /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ 70585 if (result != MA_BUSY) { 70586 if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { 70587 ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); 70588 } 70589 if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { 70590 ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); 70591 } 70592 } 70593 70594 /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ 70595 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); 70596 70597 /* A busy result should be considered successful from the point of view of the job system. */ 70598 if (result == MA_BUSY) { 70599 result = MA_SUCCESS; 70600 } 70601 70602 return result; 70603 } 70604 70605 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) 70606 { 70607 ma_resource_manager* pResourceManager; 70608 ma_resource_manager_data_buffer_node* pDataBufferNode; 70609 70610 MA_ASSERT(pJob != NULL); 70611 70612 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; 70613 MA_ASSERT(pResourceManager != NULL); 70614 70615 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; 70616 MA_ASSERT(pDataBufferNode != NULL); 70617 70618 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { 70619 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70620 } 70621 70622 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); 70623 70624 /* The event needs to be signalled last. */ 70625 if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { 70626 ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); 70627 } 70628 70629 if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { 70630 ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); 70631 } 70632 70633 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); 70634 return MA_SUCCESS; 70635 } 70636 70637 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) 70638 { 70639 ma_result result = MA_SUCCESS; 70640 ma_resource_manager* pResourceManager; 70641 ma_resource_manager_data_buffer_node* pDataBufferNode; 70642 70643 MA_ASSERT(pJob != NULL); 70644 70645 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; 70646 MA_ASSERT(pResourceManager != NULL); 70647 70648 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; 70649 MA_ASSERT(pDataBufferNode != NULL); 70650 70651 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { 70652 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70653 } 70654 70655 /* Don't do any more decoding if the data buffer has started the uninitialization process. */ 70656 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); 70657 if (result != MA_BUSY) { 70658 goto done; 70659 } 70660 70661 /* We're ready to decode the next page. */ 70662 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); 70663 70664 /* 70665 If we have a success code by this point, we want to post another job. We're going to set the 70666 result back to MA_BUSY to make it clear that there's still more to load. 70667 */ 70668 if (result == MA_SUCCESS) { 70669 ma_job newJob; 70670 newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ 70671 newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ 70672 70673 result = ma_resource_manager_post_job(pResourceManager, &newJob); 70674 70675 /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ 70676 if (result == MA_SUCCESS) { 70677 result = MA_BUSY; 70678 } 70679 } 70680 70681 done: 70682 /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ 70683 if (result != MA_BUSY) { 70684 ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); 70685 ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); 70686 } 70687 70688 /* If we reached the end we need to treat it as successful. */ 70689 if (result == MA_AT_END) { 70690 result = MA_SUCCESS; 70691 } 70692 70693 /* Make sure we set the result of node in case some error occurred. */ 70694 ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); 70695 70696 /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ 70697 if (result != MA_BUSY) { 70698 if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { 70699 ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); 70700 } 70701 70702 if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { 70703 ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); 70704 } 70705 } 70706 70707 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); 70708 return result; 70709 } 70710 70711 70712 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) 70713 { 70714 ma_result result = MA_SUCCESS; 70715 ma_resource_manager* pResourceManager; 70716 ma_resource_manager_data_buffer* pDataBuffer; 70717 ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; 70718 ma_bool32 isConnectorInitialized = MA_FALSE; 70719 70720 /* 70721 All we're doing here is checking if the node has finished loading. If not, we just re-post the job 70722 and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. 70723 */ 70724 MA_ASSERT(pJob != NULL); 70725 70726 pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; 70727 MA_ASSERT(pDataBuffer != NULL); 70728 70729 pResourceManager = pDataBuffer->pResourceManager; 70730 70731 if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { 70732 return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ 70733 } 70734 70735 /* 70736 First thing we need to do is check whether or not the data buffer is getting deleted. If so we 70737 just abort, but making sure we increment the execution pointer. 70738 */ 70739 result = ma_resource_manager_data_buffer_result(pDataBuffer); 70740 if (result != MA_BUSY) { 70741 goto done; /* <-- This will ensure the exucution pointer is incremented. */ 70742 } else { 70743 result = MA_SUCCESS; /* <-- Make sure this is reset. */ 70744 } 70745 70746 /* Try initializing the connector if we haven't already. */ 70747 isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); 70748 if (isConnectorInitialized == MA_FALSE) { 70749 dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); 70750 70751 if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { 70752 /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ 70753 ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ 70754 dataSourceConfig = ma_resource_manager_data_source_config_init(); 70755 dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; 70756 dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; 70757 dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; 70758 dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; 70759 dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; 70760 70761 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); 70762 if (result != MA_SUCCESS) { 70763 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); 70764 goto done; 70765 } 70766 } else { 70767 /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ 70768 } 70769 } else { 70770 /* The connector is already initialized. Nothing to do here. */ 70771 } 70772 70773 /* 70774 If the data node is still loading, we need to repost the job and *not* increment the execution 70775 pointer (i.e. we need to not fall through to the "done" label). 70776 70777 There is a hole between here and the where the data connector is initialized where the data 70778 buffer node may have finished initializing. We need to check for this by checking the result of 70779 the data buffer node and whether or not we had an unknown data supply type at the time of 70780 trying to initialize the data connector. 70781 */ 70782 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); 70783 if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { 70784 return ma_resource_manager_post_job(pResourceManager, pJob); 70785 } 70786 70787 done: 70788 /* Only move away from a busy code so that we don't trash any existing error codes. */ 70789 ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); 70790 70791 /* Only signal the other threads after the result has been set just for cleanliness sake. */ 70792 if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { 70793 ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); 70794 } 70795 if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { 70796 ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); 70797 } 70798 70799 /* 70800 If at this point the data buffer has not had it's connector initialized, it means the 70801 notification event was never signalled which means we need to signal it here. 70802 */ 70803 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { 70804 if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { 70805 ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); 70806 } 70807 if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { 70808 ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); 70809 } 70810 } 70811 70812 ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); 70813 return result; 70814 } 70815 70816 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) 70817 { 70818 ma_resource_manager* pResourceManager; 70819 ma_resource_manager_data_buffer* pDataBuffer; 70820 70821 MA_ASSERT(pJob != NULL); 70822 70823 pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; 70824 MA_ASSERT(pDataBuffer != NULL); 70825 70826 pResourceManager = pDataBuffer->pResourceManager; 70827 70828 if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { 70829 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70830 } 70831 70832 ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); 70833 70834 /* The event needs to be signalled last. */ 70835 if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { 70836 ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); 70837 } 70838 70839 if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { 70840 ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); 70841 } 70842 70843 ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); 70844 return MA_SUCCESS; 70845 } 70846 70847 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) 70848 { 70849 ma_result result = MA_SUCCESS; 70850 ma_decoder_config decoderConfig; 70851 ma_uint32 pageBufferSizeInBytes; 70852 ma_resource_manager* pResourceManager; 70853 ma_resource_manager_data_stream* pDataStream; 70854 70855 MA_ASSERT(pJob != NULL); 70856 70857 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; 70858 MA_ASSERT(pDataStream != NULL); 70859 70860 pResourceManager = pDataStream->pResourceManager; 70861 70862 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { 70863 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70864 } 70865 70866 if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { 70867 result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ 70868 goto done; 70869 } 70870 70871 /* We need to initialize the decoder first so we can determine the size of the pages. */ 70872 decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); 70873 70874 if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { 70875 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); 70876 } else { 70877 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); 70878 } 70879 if (result != MA_SUCCESS) { 70880 goto done; 70881 } 70882 70883 /* Retrieve the total length of the file before marking the decoder as loaded. */ 70884 if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { 70885 result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); 70886 if (result != MA_SUCCESS) { 70887 goto done; /* Failed to retrieve the length. */ 70888 } 70889 } else { 70890 pDataStream->totalLengthInPCMFrames = 0; 70891 } 70892 70893 /* 70894 Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file 70895 and we don't want to have another thread trying to access the decoder while it's scanning. 70896 */ 70897 pDataStream->isDecoderInitialized = MA_TRUE; 70898 70899 /* We have the decoder so we can now initialize our page buffer. */ 70900 pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); 70901 70902 pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); 70903 if (pDataStream->pPageData == NULL) { 70904 ma_decoder_uninit(&pDataStream->decoder); 70905 result = MA_OUT_OF_MEMORY; 70906 goto done; 70907 } 70908 70909 /* Seek to our initial seek point before filling the initial pages. */ 70910 ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); 70911 70912 /* We have our decoder and our page buffer, so now we need to fill our pages. */ 70913 ma_resource_manager_data_stream_fill_pages(pDataStream); 70914 70915 /* And now we're done. We want to make sure the result is MA_SUCCESS. */ 70916 result = MA_SUCCESS; 70917 70918 done: 70919 ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); 70920 ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); 70921 70922 /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ 70923 ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); 70924 70925 /* Only signal the other threads after the result has been set just for cleanliness sake. */ 70926 if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { 70927 ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); 70928 } 70929 if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { 70930 ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); 70931 } 70932 70933 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); 70934 return result; 70935 } 70936 70937 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) 70938 { 70939 ma_resource_manager* pResourceManager; 70940 ma_resource_manager_data_stream* pDataStream; 70941 70942 MA_ASSERT(pJob != NULL); 70943 70944 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; 70945 MA_ASSERT(pDataStream != NULL); 70946 70947 pResourceManager = pDataStream->pResourceManager; 70948 70949 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { 70950 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70951 } 70952 70953 /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ 70954 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); 70955 70956 if (pDataStream->isDecoderInitialized) { 70957 ma_decoder_uninit(&pDataStream->decoder); 70958 } 70959 70960 if (pDataStream->pPageData != NULL) { 70961 ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); 70962 pDataStream->pPageData = NULL; /* Just in case... */ 70963 } 70964 70965 ma_data_source_uninit(&pDataStream->ds); 70966 70967 /* The event needs to be signalled last. */ 70968 if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { 70969 ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); 70970 } 70971 if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { 70972 ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); 70973 } 70974 70975 /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ 70976 return MA_SUCCESS; 70977 } 70978 70979 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) 70980 { 70981 ma_result result = MA_SUCCESS; 70982 ma_resource_manager* pResourceManager; 70983 ma_resource_manager_data_stream* pDataStream; 70984 70985 MA_ASSERT(pJob != NULL); 70986 70987 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; 70988 MA_ASSERT(pDataStream != NULL); 70989 70990 pResourceManager = pDataStream->pResourceManager; 70991 70992 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { 70993 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 70994 } 70995 70996 /* For streams, the status should be MA_SUCCESS. */ 70997 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { 70998 result = MA_INVALID_OPERATION; 70999 goto done; 71000 } 71001 71002 ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); 71003 71004 done: 71005 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); 71006 return result; 71007 } 71008 71009 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) 71010 { 71011 ma_result result = MA_SUCCESS; 71012 ma_resource_manager* pResourceManager; 71013 ma_resource_manager_data_stream* pDataStream; 71014 71015 MA_ASSERT(pJob != NULL); 71016 71017 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; 71018 MA_ASSERT(pDataStream != NULL); 71019 71020 pResourceManager = pDataStream->pResourceManager; 71021 71022 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { 71023 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ 71024 } 71025 71026 /* For streams the status should be MA_SUCCESS for this to do anything. */ 71027 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { 71028 result = MA_INVALID_OPERATION; 71029 goto done; 71030 } 71031 71032 /* 71033 With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except 71034 instead of initializing the decoder, we seek to a frame. 71035 */ 71036 ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); 71037 71038 /* After seeking we'll need to reload the pages. */ 71039 ma_resource_manager_data_stream_fill_pages(pDataStream); 71040 71041 /* We need to let the public API know that we're done seeking. */ 71042 ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); 71043 71044 done: 71045 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); 71046 return result; 71047 } 71048 71049 MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) 71050 { 71051 if (pResourceManager == NULL || pJob == NULL) { 71052 return MA_INVALID_ARGS; 71053 } 71054 71055 return ma_job_process(pJob); 71056 } 71057 71058 MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) 71059 { 71060 ma_result result; 71061 ma_job job; 71062 71063 if (pResourceManager == NULL) { 71064 return MA_INVALID_ARGS; 71065 } 71066 71067 /* This will return MA_CANCELLED if the next job is a quit job. */ 71068 result = ma_resource_manager_next_job(pResourceManager, &job); 71069 if (result != MA_SUCCESS) { 71070 return result; 71071 } 71072 71073 return ma_job_process(&job); 71074 } 71075 #else 71076 /* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ 71077 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } 71078 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } 71079 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } 71080 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } 71081 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } 71082 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } 71083 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } 71084 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } 71085 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } 71086 #endif /* MA_NO_RESOURCE_MANAGER */ 71087 71088 71089 #ifndef MA_NO_NODE_GRAPH 71090 /* 10ms @ 48K = 480. Must never exceed 65535. */ 71091 #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 71092 #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 71093 #endif 71094 71095 71096 static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); 71097 71098 MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) 71099 { 71100 #ifndef MA_NO_GENERATION 71101 { 71102 ma_waveform_config waveformConfig; 71103 ma_waveform waveform; 71104 71105 waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); 71106 ma_waveform_init(&waveformConfig, &waveform); 71107 ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); 71108 } 71109 #else 71110 { 71111 (void)pFramesOut; 71112 (void)frameCount; 71113 (void)format; 71114 (void)channels; 71115 (void)sampleRate; 71116 #if defined(MA_DEBUG_OUTPUT) 71117 { 71118 #if _MSC_VER 71119 #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") 71120 #endif 71121 } 71122 #endif 71123 } 71124 #endif 71125 } 71126 71127 71128 71129 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) 71130 { 71131 ma_node_graph_config config; 71132 71133 MA_ZERO_OBJECT(&config); 71134 config.channels = channels; 71135 config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; 71136 71137 return config; 71138 } 71139 71140 71141 static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) 71142 { 71143 MA_ASSERT(pNodeGraph != NULL); 71144 ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); 71145 } 71146 71147 #if 0 71148 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) 71149 { 71150 MA_ASSERT(pNodeGraph != NULL); 71151 return ma_atomic_load_32(&pNodeGraph->isReading); 71152 } 71153 #endif 71154 71155 71156 static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 71157 { 71158 ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; 71159 ma_uint64 framesRead; 71160 71161 ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); 71162 71163 *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ 71164 71165 (void)ppFramesIn; 71166 (void)pFrameCountIn; 71167 } 71168 71169 static ma_node_vtable g_node_graph_node_vtable = 71170 { 71171 ma_node_graph_node_process_pcm_frames, 71172 NULL, /* onGetRequiredInputFrameCount */ 71173 0, /* 0 input buses. */ 71174 1, /* 1 output bus. */ 71175 0 /* Flags. */ 71176 }; 71177 71178 static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 71179 { 71180 MA_ASSERT(pNode != NULL); 71181 MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); 71182 MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); 71183 71184 /* Input channel count needs to be the same as the output channel count. */ 71185 MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); 71186 71187 /* We don't need to do anything here because it's a passthrough. */ 71188 (void)pNode; 71189 (void)ppFramesIn; 71190 (void)pFrameCountIn; 71191 (void)ppFramesOut; 71192 (void)pFrameCountOut; 71193 71194 #if 0 71195 /* The data has already been mixed. We just need to move it to the output buffer. */ 71196 if (ppFramesIn != NULL) { 71197 ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); 71198 } 71199 #endif 71200 } 71201 71202 static ma_node_vtable g_node_graph_endpoint_vtable = 71203 { 71204 ma_node_graph_endpoint_process_pcm_frames, 71205 NULL, /* onGetRequiredInputFrameCount */ 71206 1, /* 1 input bus. */ 71207 1, /* 1 output bus. */ 71208 MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ 71209 }; 71210 71211 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) 71212 { 71213 ma_result result; 71214 ma_node_config baseConfig; 71215 ma_node_config endpointConfig; 71216 71217 if (pNodeGraph == NULL) { 71218 return MA_INVALID_ARGS; 71219 } 71220 71221 MA_ZERO_OBJECT(pNodeGraph); 71222 pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; 71223 if (pNodeGraph->nodeCacheCapInFrames == 0) { 71224 pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; 71225 } 71226 71227 71228 /* Base node so we can use the node graph as a node into another graph. */ 71229 baseConfig = ma_node_config_init(); 71230 baseConfig.vtable = &g_node_graph_node_vtable; 71231 baseConfig.pOutputChannels = &pConfig->channels; 71232 71233 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); 71234 if (result != MA_SUCCESS) { 71235 return result; 71236 } 71237 71238 71239 /* Endpoint. */ 71240 endpointConfig = ma_node_config_init(); 71241 endpointConfig.vtable = &g_node_graph_endpoint_vtable; 71242 endpointConfig.pInputChannels = &pConfig->channels; 71243 endpointConfig.pOutputChannels = &pConfig->channels; 71244 71245 result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); 71246 if (result != MA_SUCCESS) { 71247 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); 71248 return result; 71249 } 71250 71251 return MA_SUCCESS; 71252 } 71253 71254 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) 71255 { 71256 if (pNodeGraph == NULL) { 71257 return; 71258 } 71259 71260 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); 71261 } 71262 71263 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) 71264 { 71265 if (pNodeGraph == NULL) { 71266 return NULL; 71267 } 71268 71269 return &pNodeGraph->endpoint; 71270 } 71271 71272 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 71273 { 71274 ma_result result = MA_SUCCESS; 71275 ma_uint64 totalFramesRead; 71276 ma_uint32 channels; 71277 71278 if (pFramesRead != NULL) { 71279 *pFramesRead = 0; /* Safety. */ 71280 } 71281 71282 if (pNodeGraph == NULL) { 71283 return MA_INVALID_ARGS; 71284 } 71285 71286 channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); 71287 71288 71289 /* We'll be nice and try to do a full read of all frameCount frames. */ 71290 totalFramesRead = 0; 71291 while (totalFramesRead < frameCount) { 71292 ma_uint32 framesJustRead; 71293 ma_uint64 framesToRead = frameCount - totalFramesRead; 71294 71295 if (framesToRead > 0xFFFFFFFF) { 71296 framesToRead = 0xFFFFFFFF; 71297 } 71298 71299 ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); 71300 { 71301 result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); 71302 } 71303 ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); 71304 71305 totalFramesRead += framesJustRead; 71306 71307 if (result != MA_SUCCESS) { 71308 break; 71309 } 71310 71311 /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ 71312 if (framesJustRead == 0) { 71313 break; 71314 } 71315 } 71316 71317 /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ 71318 if (totalFramesRead < frameCount) { 71319 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); 71320 } 71321 71322 if (pFramesRead != NULL) { 71323 *pFramesRead = totalFramesRead; 71324 } 71325 71326 return result; 71327 } 71328 71329 MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) 71330 { 71331 if (pNodeGraph == NULL) { 71332 return 0; 71333 } 71334 71335 return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); 71336 } 71337 71338 MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) 71339 { 71340 if (pNodeGraph == NULL) { 71341 return 0; 71342 } 71343 71344 return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ 71345 } 71346 71347 MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) 71348 { 71349 if (pNodeGraph == NULL) { 71350 return MA_INVALID_ARGS; 71351 } 71352 71353 return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ 71354 } 71355 71356 71357 #define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ 71358 71359 static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) 71360 { 71361 MA_ASSERT(pOutputBus != NULL); 71362 MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); 71363 MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); 71364 MA_ASSERT(channels < 256); 71365 71366 MA_ZERO_OBJECT(pOutputBus); 71367 71368 if (channels == 0) { 71369 return MA_INVALID_ARGS; 71370 } 71371 71372 pOutputBus->pNode = pNode; 71373 pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; 71374 pOutputBus->channels = (ma_uint8)channels; 71375 pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ 71376 pOutputBus->volume = 1; 71377 71378 return MA_SUCCESS; 71379 } 71380 71381 static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) 71382 { 71383 ma_spinlock_lock(&pOutputBus->lock); 71384 } 71385 71386 static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) 71387 { 71388 ma_spinlock_unlock(&pOutputBus->lock); 71389 } 71390 71391 71392 static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) 71393 { 71394 return pOutputBus->channels; 71395 } 71396 71397 71398 static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) 71399 { 71400 if (hasRead) { 71401 ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); 71402 } else { 71403 ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); 71404 } 71405 } 71406 71407 static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) 71408 { 71409 return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; 71410 } 71411 71412 71413 static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) 71414 { 71415 ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); 71416 } 71417 71418 static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) 71419 { 71420 return ma_atomic_load_32(&pOutputBus->isAttached); 71421 } 71422 71423 71424 static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) 71425 { 71426 MA_ASSERT(pOutputBus != NULL); 71427 71428 if (volume < 0.0f) { 71429 volume = 0.0f; 71430 } 71431 71432 ma_atomic_exchange_f32(&pOutputBus->volume, volume); 71433 71434 return MA_SUCCESS; 71435 } 71436 71437 static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) 71438 { 71439 return ma_atomic_load_f32((float*)&pOutputBus->volume); 71440 } 71441 71442 71443 static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) 71444 { 71445 MA_ASSERT(pInputBus != NULL); 71446 MA_ASSERT(channels < 256); 71447 71448 MA_ZERO_OBJECT(pInputBus); 71449 71450 if (channels == 0) { 71451 return MA_INVALID_ARGS; 71452 } 71453 71454 pInputBus->channels = (ma_uint8)channels; 71455 71456 return MA_SUCCESS; 71457 } 71458 71459 static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) 71460 { 71461 MA_ASSERT(pInputBus != NULL); 71462 71463 ma_spinlock_lock(&pInputBus->lock); 71464 } 71465 71466 static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) 71467 { 71468 MA_ASSERT(pInputBus != NULL); 71469 71470 ma_spinlock_unlock(&pInputBus->lock); 71471 } 71472 71473 71474 static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) 71475 { 71476 ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); 71477 } 71478 71479 static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) 71480 { 71481 ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); 71482 } 71483 71484 static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) 71485 { 71486 return ma_atomic_load_32(&pInputBus->nextCounter); 71487 } 71488 71489 71490 static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) 71491 { 71492 return pInputBus->channels; 71493 } 71494 71495 71496 static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) 71497 { 71498 MA_ASSERT(pInputBus != NULL); 71499 MA_ASSERT(pOutputBus != NULL); 71500 71501 /* 71502 Mark the output bus as detached first. This will prevent future iterations on the audio thread 71503 from iterating this output bus. 71504 */ 71505 ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); 71506 71507 /* 71508 We cannot use the output bus lock here since it'll be getting used at a higher level, but we do 71509 still need to use the input bus lock since we'll be updating pointers on two different output 71510 buses. The same rules apply here as the attaching case. Although we're using a lock here, we're 71511 *not* using a lock when iterating over the list in the audio thread. We therefore need to craft 71512 this in a way such that the iteration on the audio thread doesn't break. 71513 71514 The the first thing to do is swap out the "next" pointer of the previous output bus with the 71515 new "next" output bus. This is the operation that matters for iteration on the audio thread. 71516 After that, the previous pointer on the new "next" pointer needs to be updated, after which 71517 point the linked list will be in a good state. 71518 */ 71519 ma_node_input_bus_lock(pInputBus); 71520 { 71521 ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); 71522 ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); 71523 71524 if (pOldPrev != NULL) { 71525 ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ 71526 } 71527 if (pOldNext != NULL) { 71528 ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ 71529 } 71530 } 71531 ma_node_input_bus_unlock(pInputBus); 71532 71533 /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ 71534 ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ 71535 ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ 71536 pOutputBus->pInputNode = NULL; 71537 pOutputBus->inputNodeInputBusIndex = 0; 71538 71539 71540 /* 71541 For thread-safety reasons, we don't want to be returning from this straight away. We need to 71542 wait for the audio thread to finish with the output bus. There's two things we need to wait 71543 for. The first is the part that selects the next output bus in the list, and the other is the 71544 part that reads from the output bus. Basically all we're doing is waiting for the input bus 71545 to stop referencing the output bus. 71546 71547 We're doing this part last because we want the section above to run while the audio thread 71548 is finishing up with the output bus, just for efficiency reasons. We marked the output bus as 71549 detached right at the top of this function which is going to prevent the audio thread from 71550 iterating the output bus again. 71551 */ 71552 71553 /* Part 1: Wait for the current iteration to complete. */ 71554 while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { 71555 ma_yield(); 71556 } 71557 71558 /* Part 2: Wait for any reads to complete. */ 71559 while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { 71560 ma_yield(); 71561 } 71562 71563 /* 71564 At this point we're done detaching and we can be guaranteed that the audio thread is not going 71565 to attempt to reference this output bus again (until attached again). 71566 */ 71567 } 71568 71569 #if 0 /* Not used at the moment, but leaving here in case I need it later. */ 71570 static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) 71571 { 71572 MA_ASSERT(pInputBus != NULL); 71573 MA_ASSERT(pOutputBus != NULL); 71574 71575 ma_node_output_bus_lock(pOutputBus); 71576 { 71577 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); 71578 } 71579 ma_node_output_bus_unlock(pOutputBus); 71580 } 71581 #endif 71582 71583 static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) 71584 { 71585 MA_ASSERT(pInputBus != NULL); 71586 MA_ASSERT(pOutputBus != NULL); 71587 71588 ma_node_output_bus_lock(pOutputBus); 71589 { 71590 ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); 71591 71592 /* Detach from any existing attachment first if necessary. */ 71593 if (pOldInputNode != NULL) { 71594 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); 71595 } 71596 71597 /* 71598 At this point we can be sure the output bus is not attached to anything. The linked list in the 71599 old input bus has been updated so that pOutputBus will not get iterated again. 71600 */ 71601 pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ 71602 pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; 71603 71604 /* 71605 Now we need to attach the output bus to the linked list. This involves updating two pointers on 71606 two different output buses so I'm going to go ahead and keep this simple and just use a lock. 71607 There are ways to do this without a lock, but it's just too hard to maintain for it's value. 71608 71609 Although we're locking here, it's important to remember that we're *not* locking when iterating 71610 and reading audio data since that'll be running on the audio thread. As a result we need to be 71611 careful how we craft this so that we don't break iteration. What we're going to do is always 71612 attach the new item so that it becomes the first item in the list. That way, as we're iterating 71613 we won't break any links in the list and iteration will continue safely. The detaching case will 71614 also be crafted in a way as to not break list iteration. It's important to remember to use 71615 atomic exchanges here since no locking is happening on the audio thread during iteration. 71616 */ 71617 ma_node_input_bus_lock(pInputBus); 71618 { 71619 ma_node_output_bus* pNewPrev = &pInputBus->head; 71620 ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); 71621 71622 /* Update the local output bus. */ 71623 ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); 71624 ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); 71625 71626 /* Update the other output buses to point back to the local output bus. */ 71627 ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ 71628 71629 /* Do the previous pointer last. This is only used for detachment. */ 71630 if (pNewNext != NULL) { 71631 ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); 71632 } 71633 } 71634 ma_node_input_bus_unlock(pInputBus); 71635 71636 /* 71637 Mark the node as attached last. This is used to controlling whether or the output bus will be 71638 iterated on the audio thread. Mainly required for detachment purposes. 71639 */ 71640 ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); 71641 } 71642 ma_node_output_bus_unlock(pOutputBus); 71643 } 71644 71645 static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) 71646 { 71647 ma_node_output_bus* pNext; 71648 71649 MA_ASSERT(pInputBus != NULL); 71650 71651 if (pOutputBus == NULL) { 71652 return NULL; 71653 } 71654 71655 ma_node_input_bus_next_begin(pInputBus); 71656 { 71657 pNext = pOutputBus; 71658 for (;;) { 71659 pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); 71660 if (pNext == NULL) { 71661 break; /* Reached the end. */ 71662 } 71663 71664 if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { 71665 continue; /* The node is not attached. Keep checking. */ 71666 } 71667 71668 /* The next node has been selected. */ 71669 break; 71670 } 71671 71672 /* We need to increment the reference count of the selected node. */ 71673 if (pNext != NULL) { 71674 ma_atomic_fetch_add_32(&pNext->refCount, 1); 71675 } 71676 71677 /* The previous node is no longer being referenced. */ 71678 ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); 71679 } 71680 ma_node_input_bus_next_end(pInputBus); 71681 71682 return pNext; 71683 } 71684 71685 static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) 71686 { 71687 return ma_node_input_bus_next(pInputBus, &pInputBus->head); 71688 } 71689 71690 71691 71692 static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) 71693 { 71694 ma_result result = MA_SUCCESS; 71695 ma_node_output_bus* pOutputBus; 71696 ma_node_output_bus* pFirst; 71697 ma_uint32 inputChannels; 71698 ma_bool32 doesOutputBufferHaveContent = MA_FALSE; 71699 71700 (void)pInputNode; /* Not currently used. */ 71701 71702 /* 71703 This will be called from the audio thread which means we can't be doing any locking. Basically, 71704 this function will not perfom any locking, whereas attaching and detaching will, but crafted in 71705 such a way that we don't need to perform any locking here. The important thing to remember is 71706 to always iterate in a forward direction. 71707 71708 In order to process any data we need to first read from all input buses. That's where this 71709 function comes in. This iterates over each of the attachments and accumulates/mixes them. We 71710 also convert the channels to the nodes output channel count before mixing. We want to do this 71711 channel conversion so that the caller of this function can invoke the processing callback 71712 without having to do it themselves. 71713 71714 When we iterate over each of the attachments on the input bus, we need to read as much data as 71715 we can from each of them so that we don't end up with holes between each of the attachments. To 71716 do this, we need to read from each attachment in a loop and read as many frames as we can, up 71717 to `frameCount`. 71718 */ 71719 MA_ASSERT(pInputNode != NULL); 71720 MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ 71721 71722 *pFramesRead = 0; /* Safety. */ 71723 71724 inputChannels = ma_node_input_bus_get_channels(pInputBus); 71725 71726 /* 71727 We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They 71728 are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() 71729 once per iteration, however we have an optimization to checks whether or not it's the first item in 71730 the list. We therefore need to store a pointer to the first item rather than repeatedly calling 71731 ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it 71732 after calling ma_node_input_bus_next(), which we won't be. 71733 */ 71734 pFirst = ma_node_input_bus_first(pInputBus); 71735 if (pFirst == NULL) { 71736 return MA_SUCCESS; /* No attachments. Read nothing. */ 71737 } 71738 71739 for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { 71740 ma_uint32 framesProcessed = 0; 71741 ma_bool32 isSilentOutput = MA_FALSE; 71742 71743 MA_ASSERT(pOutputBus->pNode != NULL); 71744 MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); 71745 71746 isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; 71747 71748 if (pFramesOut != NULL) { 71749 /* Read. */ 71750 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; 71751 ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; 71752 71753 while (framesProcessed < frameCount) { 71754 float* pRunningFramesOut; 71755 ma_uint32 framesToRead; 71756 ma_uint32 framesJustRead; 71757 71758 framesToRead = frameCount - framesProcessed; 71759 if (framesToRead > tempCapInFrames) { 71760 framesToRead = tempCapInFrames; 71761 } 71762 71763 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); 71764 71765 if (doesOutputBufferHaveContent == MA_FALSE) { 71766 /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ 71767 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); 71768 } else { 71769 /* Slow path. Not the first attachment. Mixing required. */ 71770 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); 71771 if (result == MA_SUCCESS || result == MA_AT_END) { 71772 if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ 71773 ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); 71774 } 71775 } 71776 } 71777 71778 framesProcessed += framesJustRead; 71779 71780 /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ 71781 if (result != MA_SUCCESS) { 71782 break; 71783 } 71784 71785 /* If we didn't read anything, abort so we don't get stuck in a loop. */ 71786 if (framesJustRead == 0) { 71787 break; 71788 } 71789 } 71790 71791 /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ 71792 if (pOutputBus == pFirst && framesProcessed < frameCount) { 71793 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); 71794 } 71795 71796 if (isSilentOutput == MA_FALSE) { 71797 doesOutputBufferHaveContent = MA_TRUE; 71798 } 71799 } else { 71800 /* Seek. */ 71801 ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); 71802 } 71803 } 71804 71805 /* If we didn't output anything, output silence. */ 71806 if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { 71807 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); 71808 } 71809 71810 /* In this path we always "process" the entire amount. */ 71811 *pFramesRead = frameCount; 71812 71813 return result; 71814 } 71815 71816 71817 MA_API ma_node_config ma_node_config_init(void) 71818 { 71819 ma_node_config config; 71820 71821 MA_ZERO_OBJECT(&config); 71822 config.initialState = ma_node_state_started; /* Nodes are started by default. */ 71823 config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; 71824 config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; 71825 71826 return config; 71827 } 71828 71829 71830 71831 static ma_result ma_node_detach_full(ma_node* pNode); 71832 71833 static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) 71834 { 71835 ma_node_base* pNodeBase = (ma_node_base*)pNode; 71836 ma_uint32 iInputBus; 71837 float* pBasePtr; 71838 71839 MA_ASSERT(pNodeBase != NULL); 71840 71841 /* Input data is stored at the front of the buffer. */ 71842 pBasePtr = pNodeBase->pCachedData; 71843 for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { 71844 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); 71845 } 71846 71847 return pBasePtr; 71848 } 71849 71850 static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) 71851 { 71852 ma_node_base* pNodeBase = (ma_node_base*)pNode; 71853 ma_uint32 iInputBus; 71854 ma_uint32 iOutputBus; 71855 float* pBasePtr; 71856 71857 MA_ASSERT(pNodeBase != NULL); 71858 71859 /* Cached output data starts after the input data. */ 71860 pBasePtr = pNodeBase->pCachedData; 71861 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { 71862 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); 71863 } 71864 71865 for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { 71866 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); 71867 } 71868 71869 return pBasePtr; 71870 } 71871 71872 71873 typedef struct 71874 { 71875 size_t sizeInBytes; 71876 size_t inputBusOffset; 71877 size_t outputBusOffset; 71878 size_t cachedDataOffset; 71879 ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ 71880 ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ 71881 } ma_node_heap_layout; 71882 71883 static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) 71884 { 71885 ma_uint32 inputBusCount; 71886 ma_uint32 outputBusCount; 71887 71888 MA_ASSERT(pConfig != NULL); 71889 MA_ASSERT(pInputBusCount != NULL); 71890 MA_ASSERT(pOutputBusCount != NULL); 71891 71892 /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ 71893 if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { 71894 inputBusCount = pConfig->inputBusCount; 71895 } else { 71896 inputBusCount = pConfig->vtable->inputBusCount; 71897 71898 if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { 71899 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ 71900 } 71901 } 71902 71903 if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { 71904 outputBusCount = pConfig->outputBusCount; 71905 } else { 71906 outputBusCount = pConfig->vtable->outputBusCount; 71907 71908 if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { 71909 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ 71910 } 71911 } 71912 71913 /* Bus counts must be within limits. */ 71914 if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { 71915 return MA_INVALID_ARGS; 71916 } 71917 71918 71919 /* We must have channel counts for each bus. */ 71920 if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { 71921 return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ 71922 } 71923 71924 71925 /* Some special rules for passthrough nodes. */ 71926 if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { 71927 if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { 71928 return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ 71929 } 71930 71931 if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { 71932 return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ 71933 } 71934 } 71935 71936 71937 *pInputBusCount = inputBusCount; 71938 *pOutputBusCount = outputBusCount; 71939 71940 return MA_SUCCESS; 71941 } 71942 71943 static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) 71944 { 71945 ma_result result; 71946 ma_uint32 inputBusCount; 71947 ma_uint32 outputBusCount; 71948 71949 MA_ASSERT(pHeapLayout != NULL); 71950 71951 MA_ZERO_OBJECT(pHeapLayout); 71952 71953 if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { 71954 return MA_INVALID_ARGS; 71955 } 71956 71957 result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); 71958 if (result != MA_SUCCESS) { 71959 return result; 71960 } 71961 71962 pHeapLayout->sizeInBytes = 0; 71963 71964 /* Input buses. */ 71965 if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { 71966 pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; 71967 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); 71968 } else { 71969 pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ 71970 } 71971 71972 /* Output buses. */ 71973 if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { 71974 pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; 71975 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); 71976 } else { 71977 pHeapLayout->outputBusOffset = MA_SIZE_MAX; 71978 } 71979 71980 /* 71981 Cached audio data. 71982 71983 We need to allocate memory for a caching both input and output data. We have an optimization 71984 where no caching is necessary for specific conditions: 71985 71986 - The node has 0 inputs and 1 output. 71987 71988 When a node meets the above conditions, no cache is allocated. 71989 71990 The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by 71991 allocating too much, but at the same time we want it be large enough so that enough frames can 71992 be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For 71993 now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile 71994 time. It might also be worth investigating whether or not this can be configured at run time. 71995 */ 71996 if (inputBusCount == 0 && outputBusCount == 1) { 71997 /* Fast path. No cache needed. */ 71998 pHeapLayout->cachedDataOffset = MA_SIZE_MAX; 71999 } else { 72000 /* Slow path. Cache needed. */ 72001 size_t cachedDataSizeInBytes = 0; 72002 ma_uint32 iBus; 72003 72004 for (iBus = 0; iBus < inputBusCount; iBus += 1) { 72005 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); 72006 } 72007 72008 for (iBus = 0; iBus < outputBusCount; iBus += 1) { 72009 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); 72010 } 72011 72012 pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; 72013 pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); 72014 } 72015 72016 72017 /* 72018 Not technically part of the heap, but we can output the input and output bus counts so we can 72019 avoid a redundant call to ma_node_translate_bus_counts(). 72020 */ 72021 pHeapLayout->inputBusCount = inputBusCount; 72022 pHeapLayout->outputBusCount = outputBusCount; 72023 72024 /* Make sure allocation size is aligned. */ 72025 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); 72026 72027 return MA_SUCCESS; 72028 } 72029 72030 MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) 72031 { 72032 ma_result result; 72033 ma_node_heap_layout heapLayout; 72034 72035 if (pHeapSizeInBytes == NULL) { 72036 return MA_INVALID_ARGS; 72037 } 72038 72039 *pHeapSizeInBytes = 0; 72040 72041 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); 72042 if (result != MA_SUCCESS) { 72043 return result; 72044 } 72045 72046 *pHeapSizeInBytes = heapLayout.sizeInBytes; 72047 72048 return MA_SUCCESS; 72049 } 72050 72051 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) 72052 { 72053 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72054 ma_result result; 72055 ma_node_heap_layout heapLayout; 72056 ma_uint32 iInputBus; 72057 ma_uint32 iOutputBus; 72058 72059 if (pNodeBase == NULL) { 72060 return MA_INVALID_ARGS; 72061 } 72062 72063 MA_ZERO_OBJECT(pNodeBase); 72064 72065 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); 72066 if (result != MA_SUCCESS) { 72067 return result; 72068 } 72069 72070 pNodeBase->_pHeap = pHeap; 72071 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 72072 72073 pNodeBase->pNodeGraph = pNodeGraph; 72074 pNodeBase->vtable = pConfig->vtable; 72075 pNodeBase->state = pConfig->initialState; 72076 pNodeBase->stateTimes[ma_node_state_started] = 0; 72077 pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ 72078 pNodeBase->inputBusCount = heapLayout.inputBusCount; 72079 pNodeBase->outputBusCount = heapLayout.outputBusCount; 72080 72081 if (heapLayout.inputBusOffset != MA_SIZE_MAX) { 72082 pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); 72083 } else { 72084 pNodeBase->pInputBuses = pNodeBase->_inputBuses; 72085 } 72086 72087 if (heapLayout.outputBusOffset != MA_SIZE_MAX) { 72088 pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); 72089 } else { 72090 pNodeBase->pOutputBuses = pNodeBase->_outputBuses; 72091 } 72092 72093 if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { 72094 pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); 72095 pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; 72096 } else { 72097 pNodeBase->pCachedData = NULL; 72098 } 72099 72100 72101 72102 /* We need to run an initialization step for each input and output bus. */ 72103 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { 72104 result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); 72105 if (result != MA_SUCCESS) { 72106 return result; 72107 } 72108 } 72109 72110 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { 72111 result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); 72112 if (result != MA_SUCCESS) { 72113 return result; 72114 } 72115 } 72116 72117 72118 /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ 72119 if (pNodeBase->pCachedData != NULL) { 72120 ma_uint32 iBus; 72121 72122 #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ 72123 /* For safety we'll go ahead and default the buffer to silence. */ 72124 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { 72125 ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); 72126 } 72127 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { 72128 ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); 72129 } 72130 #else 72131 /* For debugging. Default to a sine wave. */ 72132 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { 72133 ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); 72134 } 72135 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { 72136 ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); 72137 } 72138 #endif 72139 } 72140 72141 return MA_SUCCESS; 72142 } 72143 72144 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) 72145 { 72146 ma_result result; 72147 size_t heapSizeInBytes; 72148 void* pHeap; 72149 72150 result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); 72151 if (result != MA_SUCCESS) { 72152 return result; 72153 } 72154 72155 if (heapSizeInBytes > 0) { 72156 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 72157 if (pHeap == NULL) { 72158 return MA_OUT_OF_MEMORY; 72159 } 72160 } else { 72161 pHeap = NULL; 72162 } 72163 72164 result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); 72165 if (result != MA_SUCCESS) { 72166 ma_free(pHeap, pAllocationCallbacks); 72167 return result; 72168 } 72169 72170 ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; 72171 return MA_SUCCESS; 72172 } 72173 72174 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 72175 { 72176 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72177 72178 if (pNodeBase == NULL) { 72179 return; 72180 } 72181 72182 /* 72183 The first thing we need to do is fully detach the node. This will detach all inputs and 72184 outputs. We need to do this first because it will sever the connection with the node graph and 72185 allow us to complete uninitialization without needing to worry about thread-safety with the 72186 audio thread. The detachment process will wait for any local processing of the node to finish. 72187 */ 72188 ma_node_detach_full(pNode); 72189 72190 /* 72191 At this point the node should be completely unreferenced by the node graph and we can finish up 72192 the uninitialization process without needing to worry about thread-safety. 72193 */ 72194 if (pNodeBase->_ownsHeap) { 72195 ma_free(pNodeBase->_pHeap, pAllocationCallbacks); 72196 } 72197 } 72198 72199 MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) 72200 { 72201 if (pNode == NULL) { 72202 return NULL; 72203 } 72204 72205 return ((const ma_node_base*)pNode)->pNodeGraph; 72206 } 72207 72208 MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) 72209 { 72210 if (pNode == NULL) { 72211 return 0; 72212 } 72213 72214 return ((ma_node_base*)pNode)->inputBusCount; 72215 } 72216 72217 MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) 72218 { 72219 if (pNode == NULL) { 72220 return 0; 72221 } 72222 72223 return ((ma_node_base*)pNode)->outputBusCount; 72224 } 72225 72226 72227 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) 72228 { 72229 const ma_node_base* pNodeBase = (const ma_node_base*)pNode; 72230 72231 if (pNode == NULL) { 72232 return 0; 72233 } 72234 72235 if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { 72236 return 0; /* Invalid bus index. */ 72237 } 72238 72239 return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); 72240 } 72241 72242 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) 72243 { 72244 const ma_node_base* pNodeBase = (const ma_node_base*)pNode; 72245 72246 if (pNode == NULL) { 72247 return 0; 72248 } 72249 72250 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { 72251 return 0; /* Invalid bus index. */ 72252 } 72253 72254 return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); 72255 } 72256 72257 72258 static ma_result ma_node_detach_full(ma_node* pNode) 72259 { 72260 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72261 ma_uint32 iInputBus; 72262 72263 if (pNodeBase == NULL) { 72264 return MA_INVALID_ARGS; 72265 } 72266 72267 /* 72268 Make sure the node is completely detached first. This will not return until the output bus is 72269 guaranteed to no longer be referenced by the audio thread. 72270 */ 72271 ma_node_detach_all_output_buses(pNode); 72272 72273 /* 72274 At this point all output buses will have been detached from the graph and we can be guaranteed 72275 that none of it's input nodes will be getting processed by the graph. We can detach these 72276 without needing to worry about the audio thread touching them. 72277 */ 72278 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { 72279 ma_node_input_bus* pInputBus; 72280 ma_node_output_bus* pOutputBus; 72281 72282 pInputBus = &pNodeBase->pInputBuses[iInputBus]; 72283 72284 /* 72285 This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those 72286 functions are specifically for the audio thread. We'll instead just manually iterate using standard 72287 linked list logic. We don't need to worry about the audio thread referencing these because the step 72288 above severed the connection to the graph. 72289 */ 72290 for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { 72291 ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ 72292 } 72293 } 72294 72295 return MA_SUCCESS; 72296 } 72297 72298 MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) 72299 { 72300 ma_result result = MA_SUCCESS; 72301 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72302 ma_node_base* pInputNodeBase; 72303 72304 if (pNode == NULL) { 72305 return MA_INVALID_ARGS; 72306 } 72307 72308 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { 72309 return MA_INVALID_ARGS; /* Invalid output bus index. */ 72310 } 72311 72312 /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ 72313 ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); 72314 { 72315 pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; 72316 if (pInputNodeBase != NULL) { 72317 ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); 72318 } 72319 } 72320 ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); 72321 72322 return result; 72323 } 72324 72325 MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) 72326 { 72327 ma_uint32 iOutputBus; 72328 72329 if (pNode == NULL) { 72330 return MA_INVALID_ARGS; 72331 } 72332 72333 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { 72334 ma_node_detach_output_bus(pNode, iOutputBus); 72335 } 72336 72337 return MA_SUCCESS; 72338 } 72339 72340 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) 72341 { 72342 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72343 ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; 72344 72345 if (pNodeBase == NULL || pOtherNodeBase == NULL) { 72346 return MA_INVALID_ARGS; 72347 } 72348 72349 if (pNodeBase == pOtherNodeBase) { 72350 return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ 72351 } 72352 72353 if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { 72354 return MA_INVALID_OPERATION; /* Invalid bus index. */ 72355 } 72356 72357 /* The output channel count of the output node must be the same as the input channel count of the input node. */ 72358 if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { 72359 return MA_INVALID_OPERATION; /* Channel count is incompatible. */ 72360 } 72361 72362 /* This will deal with detaching if the output bus is already attached to something. */ 72363 ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); 72364 72365 return MA_SUCCESS; 72366 } 72367 72368 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) 72369 { 72370 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72371 72372 if (pNodeBase == NULL) { 72373 return MA_INVALID_ARGS; 72374 } 72375 72376 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { 72377 return MA_INVALID_ARGS; /* Invalid bus index. */ 72378 } 72379 72380 return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); 72381 } 72382 72383 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) 72384 { 72385 const ma_node_base* pNodeBase = (const ma_node_base*)pNode; 72386 72387 if (pNodeBase == NULL) { 72388 return 0; 72389 } 72390 72391 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { 72392 return 0; /* Invalid bus index. */ 72393 } 72394 72395 return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); 72396 } 72397 72398 MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) 72399 { 72400 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72401 72402 if (pNodeBase == NULL) { 72403 return MA_INVALID_ARGS; 72404 } 72405 72406 ma_atomic_exchange_i32(&pNodeBase->state, state); 72407 72408 return MA_SUCCESS; 72409 } 72410 72411 MA_API ma_node_state ma_node_get_state(const ma_node* pNode) 72412 { 72413 const ma_node_base* pNodeBase = (const ma_node_base*)pNode; 72414 72415 if (pNodeBase == NULL) { 72416 return ma_node_state_stopped; 72417 } 72418 72419 return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); 72420 } 72421 72422 MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) 72423 { 72424 if (pNode == NULL) { 72425 return MA_INVALID_ARGS; 72426 } 72427 72428 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ 72429 if (state != ma_node_state_started && state != ma_node_state_stopped) { 72430 return MA_INVALID_ARGS; 72431 } 72432 72433 ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); 72434 72435 return MA_SUCCESS; 72436 } 72437 72438 MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) 72439 { 72440 if (pNode == NULL) { 72441 return 0; 72442 } 72443 72444 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ 72445 if (state != ma_node_state_started && state != ma_node_state_stopped) { 72446 return 0; 72447 } 72448 72449 return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); 72450 } 72451 72452 MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) 72453 { 72454 if (pNode == NULL) { 72455 return ma_node_state_stopped; 72456 } 72457 72458 return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); 72459 } 72460 72461 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) 72462 { 72463 ma_node_state state; 72464 72465 if (pNode == NULL) { 72466 return ma_node_state_stopped; 72467 } 72468 72469 state = ma_node_get_state(pNode); 72470 72471 /* An explicitly stopped node is always stopped. */ 72472 if (state == ma_node_state_stopped) { 72473 return ma_node_state_stopped; 72474 } 72475 72476 /* 72477 Getting here means the node is marked as started, but it may still not be truly started due to 72478 it's start time not having been reached yet. Also, the stop time may have also been reached in 72479 which case it'll be considered stopped. 72480 */ 72481 if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { 72482 return ma_node_state_stopped; /* Start time has not yet been reached. */ 72483 } 72484 72485 if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { 72486 return ma_node_state_stopped; /* Stop time has been reached. */ 72487 } 72488 72489 /* Getting here means the node is marked as started and is within it's start/stop times. */ 72490 return ma_node_state_started; 72491 } 72492 72493 MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) 72494 { 72495 if (pNode == NULL) { 72496 return 0; 72497 } 72498 72499 return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); 72500 } 72501 72502 MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) 72503 { 72504 if (pNode == NULL) { 72505 return MA_INVALID_ARGS; 72506 } 72507 72508 ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); 72509 72510 return MA_SUCCESS; 72511 } 72512 72513 72514 72515 static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 72516 { 72517 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72518 72519 MA_ASSERT(pNode != NULL); 72520 72521 if (pNodeBase->vtable->onProcess) { 72522 pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); 72523 } 72524 } 72525 72526 static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) 72527 { 72528 ma_node_base* pNodeBase = (ma_node_base*)pNode; 72529 ma_result result = MA_SUCCESS; 72530 ma_uint32 iInputBus; 72531 ma_uint32 iOutputBus; 72532 ma_uint32 inputBusCount; 72533 ma_uint32 outputBusCount; 72534 ma_uint32 totalFramesRead = 0; 72535 float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; 72536 float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; 72537 ma_uint64 globalTimeBeg; 72538 ma_uint64 globalTimeEnd; 72539 ma_uint64 startTime; 72540 ma_uint64 stopTime; 72541 ma_uint32 timeOffsetBeg; 72542 ma_uint32 timeOffsetEnd; 72543 ma_uint32 frameCountIn; 72544 ma_uint32 frameCountOut; 72545 72546 /* 72547 pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and 72548 expected that the number of frames read may be different to that requested. Therefore, the caller 72549 must look at this value to correctly determine how many frames were read. 72550 */ 72551 MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ 72552 if (pFramesRead == NULL) { 72553 return MA_INVALID_ARGS; 72554 } 72555 72556 *pFramesRead = 0; /* Safety. */ 72557 72558 if (pNodeBase == NULL) { 72559 return MA_INVALID_ARGS; 72560 } 72561 72562 if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { 72563 return MA_INVALID_ARGS; /* Invalid output bus index. */ 72564 } 72565 72566 /* Don't do anything if we're in a stopped state. */ 72567 if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { 72568 return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ 72569 } 72570 72571 72572 globalTimeBeg = globalTime; 72573 globalTimeEnd = globalTime + frameCount; 72574 startTime = ma_node_get_state_time(pNode, ma_node_state_started); 72575 stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); 72576 72577 /* 72578 At this point we know that we are inside our start/stop times. However, we may need to adjust 72579 our frame count and output pointer to accommodate since we could be straddling the time period 72580 that this function is getting called for. 72581 72582 It's possible (and likely) that the start time does not line up with the output buffer. We 72583 therefore need to offset it by a number of frames to accommodate. The same thing applies for 72584 the stop time. 72585 */ 72586 timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; 72587 timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; 72588 72589 /* Trim based on the start offset. We need to silence the start of the buffer. */ 72590 if (timeOffsetBeg > 0) { 72591 ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); 72592 pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); 72593 frameCount -= timeOffsetBeg; 72594 } 72595 72596 /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ 72597 if (timeOffsetEnd > 0) { 72598 frameCount -= timeOffsetEnd; 72599 } 72600 72601 72602 /* We run on different paths depending on the bus counts. */ 72603 inputBusCount = ma_node_get_input_bus_count(pNode); 72604 outputBusCount = ma_node_get_output_bus_count(pNode); 72605 72606 /* 72607 Run a simplified path when there are no inputs and one output. In this case there's nothing to 72608 actually read and we can go straight to output. This is a very common scenario because the vast 72609 majority of data source nodes will use this setup so this optimization I think is worthwhile. 72610 */ 72611 if (inputBusCount == 0 && outputBusCount == 1) { 72612 /* Fast path. No need to read from input and no need for any caching. */ 72613 frameCountIn = 0; 72614 frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ 72615 72616 ppFramesOut[0] = pFramesOut; 72617 72618 /* 72619 If it's a passthrough we won't be expecting the callback to output anything, so we'll 72620 need to pre-silence the output buffer. 72621 */ 72622 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { 72623 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); 72624 } 72625 72626 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); 72627 totalFramesRead = frameCountOut; 72628 } else { 72629 /* Slow path. Need to read input data. */ 72630 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { 72631 /* 72632 Fast path. We're running a passthrough. We need to read directly into the output buffer, but 72633 still fire the callback so that event handling and trigger nodes can do their thing. Since 72634 it's a passthrough there's no need for any kind of caching logic. 72635 */ 72636 MA_ASSERT(outputBusCount == inputBusCount); 72637 MA_ASSERT(outputBusCount == 1); 72638 MA_ASSERT(outputBusIndex == 0); 72639 72640 /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ 72641 ppFramesOut[0] = pFramesOut; 72642 ppFramesIn[0] = ppFramesOut[0]; 72643 72644 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); 72645 if (result == MA_SUCCESS) { 72646 /* Even though it's a passthrough, we still need to fire the callback. */ 72647 frameCountIn = totalFramesRead; 72648 frameCountOut = totalFramesRead; 72649 72650 if (totalFramesRead > 0) { 72651 ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ 72652 } 72653 72654 /* 72655 A passthrough should never have modified the input and output frame counts. If you're 72656 triggering these assers you need to fix your processing callback. 72657 */ 72658 MA_ASSERT(frameCountIn == totalFramesRead); 72659 MA_ASSERT(frameCountOut == totalFramesRead); 72660 } 72661 } else { 72662 /* Slow path. Need to do caching. */ 72663 ma_uint32 framesToProcessIn; 72664 ma_uint32 framesToProcessOut; 72665 ma_bool32 consumeNullInput = MA_FALSE; 72666 72667 /* 72668 We use frameCount as a basis for the number of frames to read since that's what's being 72669 requested, however we still need to clamp it to whatever can fit in the cache. 72670 72671 This will also be used as the basis for determining how many input frames to read. This is 72672 not ideal because it can result in too many input frames being read which introduces latency. 72673 To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount 72674 which is used as hint to miniaudio as to how many input frames it needs to read at a time. This 72675 callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. 72676 72677 This function will be called multiple times for each period of time, once for each output node. 72678 We cannot read from each input node each time this function is called. Instead we need to check 72679 whether or not this is first output bus to be read from for this time period, and if so, read 72680 from our input data. 72681 72682 To determine whether or not we're ready to read data, we check a flag. There will be one flag 72683 for each output. When the flag is set, it means data has been read previously and that we're 72684 ready to advance time forward for our input nodes by reading fresh data. 72685 */ 72686 framesToProcessOut = frameCount; 72687 if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { 72688 framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; 72689 } 72690 72691 framesToProcessIn = frameCount; 72692 if (pNodeBase->vtable->onGetRequiredInputFrameCount) { 72693 pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ 72694 } 72695 if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { 72696 framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; 72697 } 72698 72699 72700 MA_ASSERT(framesToProcessIn <= 0xFFFF); 72701 MA_ASSERT(framesToProcessOut <= 0xFFFF); 72702 72703 if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { 72704 /* Getting here means we need to do another round of processing. */ 72705 pNodeBase->cachedFrameCountOut = 0; 72706 72707 for (;;) { 72708 frameCountOut = 0; 72709 72710 /* 72711 We need to prepare our output frame pointers for processing. In the same iteration we need 72712 to mark every output bus as unread so that future calls to this function for different buses 72713 for the current time period don't pull in data when they should instead be reading from cache. 72714 */ 72715 for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { 72716 ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ 72717 ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); 72718 } 72719 72720 /* We only need to read from input buses if there isn't already some data in the cache. */ 72721 if (pNodeBase->cachedFrameCountIn == 0) { 72722 ma_uint32 maxFramesReadIn = 0; 72723 72724 /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ 72725 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { 72726 ma_uint32 framesRead; 72727 72728 /* The first thing to do is get the offset within our bulk allocation to store this input data. */ 72729 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); 72730 72731 /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ 72732 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); 72733 if (result != MA_SUCCESS) { 72734 /* It doesn't really matter if we fail because we'll just fill with silence. */ 72735 framesRead = 0; /* Just for safety, but I don't think it's really needed. */ 72736 } 72737 72738 /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ 72739 /* Any leftover frames need to silenced for safety. */ 72740 if (framesRead < framesToProcessIn) { 72741 ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); 72742 } 72743 72744 maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); 72745 } 72746 72747 /* This was a fresh load of input data so reset our consumption counter. */ 72748 pNodeBase->consumedFrameCountIn = 0; 72749 72750 /* 72751 We don't want to keep processing if there's nothing to process, so set the number of cached 72752 input frames to the maximum number we read from each attachment (the lesser will be padded 72753 with silence). If we didn't read anything, this will be set to 0 and the entire buffer will 72754 have been assigned to silence. This being equal to 0 is an important property for us because 72755 it allows us to detect when NULL can be passed into the processing callback for the input 72756 buffer for the purpose of continuous processing. 72757 */ 72758 pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; 72759 } else { 72760 /* We don't need to read anything, but we do need to prepare our input frame pointers. */ 72761 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { 72762 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); 72763 } 72764 } 72765 72766 /* 72767 At this point we have our input data so now we need to do some processing. Sneaky little 72768 optimization here - we can set the pointer to the output buffer for this output bus so 72769 that the final copy into the output buffer is done directly by onProcess(). 72770 */ 72771 if (pFramesOut != NULL) { 72772 ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); 72773 } 72774 72775 72776 /* Give the processing function the entire capacity of the output buffer. */ 72777 frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); 72778 72779 /* 72780 We need to treat nodes with continuous processing a little differently. For these ones, 72781 we always want to fire the callback with the requested number of frames, regardless of 72782 pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass 72783 in NULL for the input buffer to the callback. 72784 */ 72785 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { 72786 /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ 72787 frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ 72788 72789 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { 72790 consumeNullInput = MA_TRUE; 72791 } else { 72792 consumeNullInput = MA_FALSE; 72793 } 72794 72795 /* 72796 Since we're using continuous processing we're always passing in a full frame count 72797 regardless of how much input data was read. If this is greater than what we read as 72798 input, we'll end up with an underflow. We instead need to make sure our cached frame 72799 count is set to the number of frames we'll be passing to the data callback. Not 72800 doing this will result in an underflow when we "consume" the cached data later on. 72801 72802 Note that this check needs to be done after the "consumeNullInput" check above because 72803 we use the property of cachedFrameCountIn being 0 to determine whether or not we 72804 should be passing in a null pointer to the processing callback for when the node is 72805 configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. 72806 */ 72807 if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { 72808 pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; 72809 } 72810 } else { 72811 frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ 72812 consumeNullInput = MA_FALSE; 72813 } 72814 72815 /* 72816 Process data slightly differently depending on whether or not we're consuming NULL 72817 input (checked just above). 72818 */ 72819 if (consumeNullInput) { 72820 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); 72821 } else { 72822 /* 72823 We want to skip processing if there's no input data, but we can only do that safely if 72824 we know that there is no chance of any output frames being produced. If continuous 72825 processing is being used, this won't be a problem because the input frame count will 72826 always be non-0. However, if continuous processing is *not* enabled and input and output 72827 data is processed at different rates, we still need to process that last input frame 72828 because there could be a few excess output frames needing to be produced from cached 72829 data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for 72830 determining whether or not we need to process the node even when there are no input 72831 frames available right now. 72832 */ 72833 if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { 72834 ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ 72835 } else { 72836 frameCountOut = 0; /* No data was processed. */ 72837 } 72838 } 72839 72840 /* 72841 Thanks to our sneaky optimization above we don't need to do any data copying directly into 72842 the output buffer - the onProcess() callback just did that for us. We do, however, need to 72843 apply the number of input and output frames that were processed. Note that due to continuous 72844 processing above, we need to do explicit checks here. If we just consumed a NULL input 72845 buffer it means that no actual input data was processed from the internal buffers and we 72846 don't want to be modifying any counters. 72847 */ 72848 if (consumeNullInput == MA_FALSE) { 72849 pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; 72850 pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; 72851 } 72852 72853 /* The cached output frame count is always equal to what we just read. */ 72854 pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; 72855 72856 /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ 72857 if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { 72858 break; 72859 } 72860 } 72861 } else { 72862 /* 72863 We're not needing to read anything from the input buffer so just read directly from our 72864 already-processed data. 72865 */ 72866 if (pFramesOut != NULL) { 72867 ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); 72868 } 72869 } 72870 72871 /* The number of frames read is always equal to the number of cached output frames. */ 72872 totalFramesRead = pNodeBase->cachedFrameCountOut; 72873 72874 /* Now that we've read the data, make sure our read flag is set. */ 72875 ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); 72876 } 72877 } 72878 72879 /* Apply volume, if necessary. */ 72880 ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); 72881 72882 /* Advance our local time forward. */ 72883 ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); 72884 72885 *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ 72886 return result; 72887 } 72888 72889 72890 72891 72892 /* Data source node. */ 72893 MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) 72894 { 72895 ma_data_source_node_config config; 72896 72897 MA_ZERO_OBJECT(&config); 72898 config.nodeConfig = ma_node_config_init(); 72899 config.pDataSource = pDataSource; 72900 72901 return config; 72902 } 72903 72904 72905 static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 72906 { 72907 ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; 72908 ma_format format; 72909 ma_uint32 channels; 72910 ma_uint32 frameCount; 72911 ma_uint64 framesRead = 0; 72912 72913 MA_ASSERT(pDataSourceNode != NULL); 72914 MA_ASSERT(pDataSourceNode->pDataSource != NULL); 72915 MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); 72916 MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); 72917 72918 /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ 72919 (void)ppFramesIn; 72920 (void)pFrameCountIn; 72921 72922 frameCount = *pFrameCountOut; 72923 72924 /* miniaudio should never be calling this with a frame count of zero. */ 72925 MA_ASSERT(frameCount > 0); 72926 72927 if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ 72928 /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ 72929 MA_ASSERT(format == ma_format_f32); 72930 (void)format; /* Just to silence some static analysis tools. */ 72931 72932 ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); 72933 } 72934 72935 *pFrameCountOut = (ma_uint32)framesRead; 72936 } 72937 72938 static ma_node_vtable g_ma_data_source_node_vtable = 72939 { 72940 ma_data_source_node_process_pcm_frames, 72941 NULL, /* onGetRequiredInputFrameCount */ 72942 0, /* 0 input buses. */ 72943 1, /* 1 output bus. */ 72944 0 72945 }; 72946 72947 MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) 72948 { 72949 ma_result result; 72950 ma_format format; /* For validating the format, which must be ma_format_f32. */ 72951 ma_uint32 channels; /* For specifying the channel count of the output bus. */ 72952 ma_node_config baseConfig; 72953 72954 if (pDataSourceNode == NULL) { 72955 return MA_INVALID_ARGS; 72956 } 72957 72958 MA_ZERO_OBJECT(pDataSourceNode); 72959 72960 if (pConfig == NULL) { 72961 return MA_INVALID_ARGS; 72962 } 72963 72964 result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ 72965 if (result != MA_SUCCESS) { 72966 return result; 72967 } 72968 72969 MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ 72970 if (format != ma_format_f32) { 72971 return MA_INVALID_ARGS; /* Invalid format. */ 72972 } 72973 72974 /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ 72975 baseConfig = pConfig->nodeConfig; 72976 baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ 72977 72978 /* 72979 The channel count is defined by the data source. It is invalid for the caller to manually set 72980 the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the 72981 channel count pointer to NULL which is how it must remain. If you trigger any of these asserts 72982 it means you're explicitly setting the channel count. Instead, configure the output channel 72983 count of your data source to be the necessary channel count. 72984 */ 72985 if (baseConfig.pOutputChannels != NULL) { 72986 return MA_INVALID_ARGS; 72987 } 72988 72989 baseConfig.pOutputChannels = &channels; 72990 72991 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); 72992 if (result != MA_SUCCESS) { 72993 return result; 72994 } 72995 72996 pDataSourceNode->pDataSource = pConfig->pDataSource; 72997 72998 return MA_SUCCESS; 72999 } 73000 73001 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) 73002 { 73003 ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); 73004 } 73005 73006 MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) 73007 { 73008 if (pDataSourceNode == NULL) { 73009 return MA_INVALID_ARGS; 73010 } 73011 73012 return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); 73013 } 73014 73015 MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) 73016 { 73017 if (pDataSourceNode == NULL) { 73018 return MA_FALSE; 73019 } 73020 73021 return ma_data_source_is_looping(pDataSourceNode->pDataSource); 73022 } 73023 73024 73025 73026 /* Splitter Node. */ 73027 MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) 73028 { 73029 ma_splitter_node_config config; 73030 73031 MA_ZERO_OBJECT(&config); 73032 config.nodeConfig = ma_node_config_init(); 73033 config.channels = channels; 73034 config.outputBusCount = 2; 73035 73036 return config; 73037 } 73038 73039 73040 static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73041 { 73042 ma_node_base* pNodeBase = (ma_node_base*)pNode; 73043 ma_uint32 iOutputBus; 73044 ma_uint32 channels; 73045 73046 MA_ASSERT(pNodeBase != NULL); 73047 MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); 73048 73049 /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ 73050 (void)pFrameCountIn; 73051 73052 /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ 73053 channels = ma_node_get_input_channels(pNodeBase, 0); 73054 73055 /* Splitting is just copying the first input bus and copying it over to each output bus. */ 73056 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { 73057 ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); 73058 } 73059 } 73060 73061 static ma_node_vtable g_ma_splitter_node_vtable = 73062 { 73063 ma_splitter_node_process_pcm_frames, 73064 NULL, /* onGetRequiredInputFrameCount */ 73065 1, /* 1 input bus. */ 73066 MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ 73067 0 73068 }; 73069 73070 MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) 73071 { 73072 ma_result result; 73073 ma_node_config baseConfig; 73074 ma_uint32 pInputChannels[1]; 73075 ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; 73076 ma_uint32 iOutputBus; 73077 73078 if (pSplitterNode == NULL) { 73079 return MA_INVALID_ARGS; 73080 } 73081 73082 MA_ZERO_OBJECT(pSplitterNode); 73083 73084 if (pConfig == NULL) { 73085 return MA_INVALID_ARGS; 73086 } 73087 73088 if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { 73089 return MA_INVALID_ARGS; /* Too many output buses. */ 73090 } 73091 73092 /* Splitters require the same number of channels between inputs and outputs. */ 73093 pInputChannels[0] = pConfig->channels; 73094 for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { 73095 pOutputChannels[iOutputBus] = pConfig->channels; 73096 } 73097 73098 baseConfig = pConfig->nodeConfig; 73099 baseConfig.vtable = &g_ma_splitter_node_vtable; 73100 baseConfig.pInputChannels = pInputChannels; 73101 baseConfig.pOutputChannels = pOutputChannels; 73102 baseConfig.outputBusCount = pConfig->outputBusCount; 73103 73104 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); 73105 if (result != MA_SUCCESS) { 73106 return result; /* Failed to initialize the base node. */ 73107 } 73108 73109 return MA_SUCCESS; 73110 } 73111 73112 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) 73113 { 73114 ma_node_uninit(pSplitterNode, pAllocationCallbacks); 73115 } 73116 73117 73118 /* 73119 Biquad Node 73120 */ 73121 MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) 73122 { 73123 ma_biquad_node_config config; 73124 73125 config.nodeConfig = ma_node_config_init(); 73126 config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); 73127 73128 return config; 73129 } 73130 73131 static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73132 { 73133 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; 73134 73135 MA_ASSERT(pNode != NULL); 73136 (void)pFrameCountIn; 73137 73138 ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73139 } 73140 73141 static ma_node_vtable g_ma_biquad_node_vtable = 73142 { 73143 ma_biquad_node_process_pcm_frames, 73144 NULL, /* onGetRequiredInputFrameCount */ 73145 1, /* One input. */ 73146 1, /* One output. */ 73147 0 /* Default flags. */ 73148 }; 73149 73150 MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) 73151 { 73152 ma_result result; 73153 ma_node_config baseNodeConfig; 73154 73155 if (pNode == NULL) { 73156 return MA_INVALID_ARGS; 73157 } 73158 73159 MA_ZERO_OBJECT(pNode); 73160 73161 if (pConfig == NULL) { 73162 return MA_INVALID_ARGS; 73163 } 73164 73165 if (pConfig->biquad.format != ma_format_f32) { 73166 return MA_INVALID_ARGS; /* The format must be f32. */ 73167 } 73168 73169 result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); 73170 if (result != MA_SUCCESS) { 73171 return result; 73172 } 73173 73174 baseNodeConfig = ma_node_config_init(); 73175 baseNodeConfig.vtable = &g_ma_biquad_node_vtable; 73176 baseNodeConfig.pInputChannels = &pConfig->biquad.channels; 73177 baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; 73178 73179 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73180 if (result != MA_SUCCESS) { 73181 return result; 73182 } 73183 73184 return result; 73185 } 73186 73187 MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) 73188 { 73189 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; 73190 73191 MA_ASSERT(pNode != NULL); 73192 73193 return ma_biquad_reinit(pConfig, &pLPFNode->biquad); 73194 } 73195 73196 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73197 { 73198 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; 73199 73200 if (pNode == NULL) { 73201 return; 73202 } 73203 73204 ma_node_uninit(pNode, pAllocationCallbacks); 73205 ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); 73206 } 73207 73208 73209 73210 /* 73211 Low Pass Filter Node 73212 */ 73213 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 73214 { 73215 ma_lpf_node_config config; 73216 73217 config.nodeConfig = ma_node_config_init(); 73218 config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); 73219 73220 return config; 73221 } 73222 73223 static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73224 { 73225 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; 73226 73227 MA_ASSERT(pNode != NULL); 73228 (void)pFrameCountIn; 73229 73230 ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73231 } 73232 73233 static ma_node_vtable g_ma_lpf_node_vtable = 73234 { 73235 ma_lpf_node_process_pcm_frames, 73236 NULL, /* onGetRequiredInputFrameCount */ 73237 1, /* One input. */ 73238 1, /* One output. */ 73239 0 /* Default flags. */ 73240 }; 73241 73242 MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) 73243 { 73244 ma_result result; 73245 ma_node_config baseNodeConfig; 73246 73247 if (pNode == NULL) { 73248 return MA_INVALID_ARGS; 73249 } 73250 73251 MA_ZERO_OBJECT(pNode); 73252 73253 if (pConfig == NULL) { 73254 return MA_INVALID_ARGS; 73255 } 73256 73257 if (pConfig->lpf.format != ma_format_f32) { 73258 return MA_INVALID_ARGS; /* The format must be f32. */ 73259 } 73260 73261 result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); 73262 if (result != MA_SUCCESS) { 73263 return result; 73264 } 73265 73266 baseNodeConfig = ma_node_config_init(); 73267 baseNodeConfig.vtable = &g_ma_lpf_node_vtable; 73268 baseNodeConfig.pInputChannels = &pConfig->lpf.channels; 73269 baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; 73270 73271 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73272 if (result != MA_SUCCESS) { 73273 return result; 73274 } 73275 73276 return result; 73277 } 73278 73279 MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) 73280 { 73281 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; 73282 73283 if (pNode == NULL) { 73284 return MA_INVALID_ARGS; 73285 } 73286 73287 return ma_lpf_reinit(pConfig, &pLPFNode->lpf); 73288 } 73289 73290 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73291 { 73292 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; 73293 73294 if (pNode == NULL) { 73295 return; 73296 } 73297 73298 ma_node_uninit(pNode, pAllocationCallbacks); 73299 ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); 73300 } 73301 73302 73303 73304 /* 73305 High Pass Filter Node 73306 */ 73307 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 73308 { 73309 ma_hpf_node_config config; 73310 73311 config.nodeConfig = ma_node_config_init(); 73312 config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); 73313 73314 return config; 73315 } 73316 73317 static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73318 { 73319 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; 73320 73321 MA_ASSERT(pNode != NULL); 73322 (void)pFrameCountIn; 73323 73324 ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73325 } 73326 73327 static ma_node_vtable g_ma_hpf_node_vtable = 73328 { 73329 ma_hpf_node_process_pcm_frames, 73330 NULL, /* onGetRequiredInputFrameCount */ 73331 1, /* One input. */ 73332 1, /* One output. */ 73333 0 /* Default flags. */ 73334 }; 73335 73336 MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) 73337 { 73338 ma_result result; 73339 ma_node_config baseNodeConfig; 73340 73341 if (pNode == NULL) { 73342 return MA_INVALID_ARGS; 73343 } 73344 73345 MA_ZERO_OBJECT(pNode); 73346 73347 if (pConfig == NULL) { 73348 return MA_INVALID_ARGS; 73349 } 73350 73351 if (pConfig->hpf.format != ma_format_f32) { 73352 return MA_INVALID_ARGS; /* The format must be f32. */ 73353 } 73354 73355 result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); 73356 if (result != MA_SUCCESS) { 73357 return result; 73358 } 73359 73360 baseNodeConfig = ma_node_config_init(); 73361 baseNodeConfig.vtable = &g_ma_hpf_node_vtable; 73362 baseNodeConfig.pInputChannels = &pConfig->hpf.channels; 73363 baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; 73364 73365 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73366 if (result != MA_SUCCESS) { 73367 return result; 73368 } 73369 73370 return result; 73371 } 73372 73373 MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) 73374 { 73375 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; 73376 73377 if (pNode == NULL) { 73378 return MA_INVALID_ARGS; 73379 } 73380 73381 return ma_hpf_reinit(pConfig, &pHPFNode->hpf); 73382 } 73383 73384 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73385 { 73386 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; 73387 73388 if (pNode == NULL) { 73389 return; 73390 } 73391 73392 ma_node_uninit(pNode, pAllocationCallbacks); 73393 ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); 73394 } 73395 73396 73397 73398 73399 /* 73400 Band Pass Filter Node 73401 */ 73402 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) 73403 { 73404 ma_bpf_node_config config; 73405 73406 config.nodeConfig = ma_node_config_init(); 73407 config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); 73408 73409 return config; 73410 } 73411 73412 static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73413 { 73414 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; 73415 73416 MA_ASSERT(pNode != NULL); 73417 (void)pFrameCountIn; 73418 73419 ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73420 } 73421 73422 static ma_node_vtable g_ma_bpf_node_vtable = 73423 { 73424 ma_bpf_node_process_pcm_frames, 73425 NULL, /* onGetRequiredInputFrameCount */ 73426 1, /* One input. */ 73427 1, /* One output. */ 73428 0 /* Default flags. */ 73429 }; 73430 73431 MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) 73432 { 73433 ma_result result; 73434 ma_node_config baseNodeConfig; 73435 73436 if (pNode == NULL) { 73437 return MA_INVALID_ARGS; 73438 } 73439 73440 MA_ZERO_OBJECT(pNode); 73441 73442 if (pConfig == NULL) { 73443 return MA_INVALID_ARGS; 73444 } 73445 73446 if (pConfig->bpf.format != ma_format_f32) { 73447 return MA_INVALID_ARGS; /* The format must be f32. */ 73448 } 73449 73450 result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); 73451 if (result != MA_SUCCESS) { 73452 return result; 73453 } 73454 73455 baseNodeConfig = ma_node_config_init(); 73456 baseNodeConfig.vtable = &g_ma_bpf_node_vtable; 73457 baseNodeConfig.pInputChannels = &pConfig->bpf.channels; 73458 baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; 73459 73460 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73461 if (result != MA_SUCCESS) { 73462 return result; 73463 } 73464 73465 return result; 73466 } 73467 73468 MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) 73469 { 73470 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; 73471 73472 if (pNode == NULL) { 73473 return MA_INVALID_ARGS; 73474 } 73475 73476 return ma_bpf_reinit(pConfig, &pBPFNode->bpf); 73477 } 73478 73479 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73480 { 73481 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; 73482 73483 if (pNode == NULL) { 73484 return; 73485 } 73486 73487 ma_node_uninit(pNode, pAllocationCallbacks); 73488 ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); 73489 } 73490 73491 73492 73493 /* 73494 Notching Filter Node 73495 */ 73496 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) 73497 { 73498 ma_notch_node_config config; 73499 73500 config.nodeConfig = ma_node_config_init(); 73501 config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); 73502 73503 return config; 73504 } 73505 73506 static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73507 { 73508 ma_notch_node* pBPFNode = (ma_notch_node*)pNode; 73509 73510 MA_ASSERT(pNode != NULL); 73511 (void)pFrameCountIn; 73512 73513 ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73514 } 73515 73516 static ma_node_vtable g_ma_notch_node_vtable = 73517 { 73518 ma_notch_node_process_pcm_frames, 73519 NULL, /* onGetRequiredInputFrameCount */ 73520 1, /* One input. */ 73521 1, /* One output. */ 73522 0 /* Default flags. */ 73523 }; 73524 73525 MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) 73526 { 73527 ma_result result; 73528 ma_node_config baseNodeConfig; 73529 73530 if (pNode == NULL) { 73531 return MA_INVALID_ARGS; 73532 } 73533 73534 MA_ZERO_OBJECT(pNode); 73535 73536 if (pConfig == NULL) { 73537 return MA_INVALID_ARGS; 73538 } 73539 73540 if (pConfig->notch.format != ma_format_f32) { 73541 return MA_INVALID_ARGS; /* The format must be f32. */ 73542 } 73543 73544 result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); 73545 if (result != MA_SUCCESS) { 73546 return result; 73547 } 73548 73549 baseNodeConfig = ma_node_config_init(); 73550 baseNodeConfig.vtable = &g_ma_notch_node_vtable; 73551 baseNodeConfig.pInputChannels = &pConfig->notch.channels; 73552 baseNodeConfig.pOutputChannels = &pConfig->notch.channels; 73553 73554 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73555 if (result != MA_SUCCESS) { 73556 return result; 73557 } 73558 73559 return result; 73560 } 73561 73562 MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) 73563 { 73564 ma_notch_node* pNotchNode = (ma_notch_node*)pNode; 73565 73566 if (pNode == NULL) { 73567 return MA_INVALID_ARGS; 73568 } 73569 73570 return ma_notch2_reinit(pConfig, &pNotchNode->notch); 73571 } 73572 73573 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73574 { 73575 ma_notch_node* pNotchNode = (ma_notch_node*)pNode; 73576 73577 if (pNode == NULL) { 73578 return; 73579 } 73580 73581 ma_node_uninit(pNode, pAllocationCallbacks); 73582 ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); 73583 } 73584 73585 73586 73587 /* 73588 Peaking Filter Node 73589 */ 73590 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) 73591 { 73592 ma_peak_node_config config; 73593 73594 config.nodeConfig = ma_node_config_init(); 73595 config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); 73596 73597 return config; 73598 } 73599 73600 static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73601 { 73602 ma_peak_node* pBPFNode = (ma_peak_node*)pNode; 73603 73604 MA_ASSERT(pNode != NULL); 73605 (void)pFrameCountIn; 73606 73607 ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73608 } 73609 73610 static ma_node_vtable g_ma_peak_node_vtable = 73611 { 73612 ma_peak_node_process_pcm_frames, 73613 NULL, /* onGetRequiredInputFrameCount */ 73614 1, /* One input. */ 73615 1, /* One output. */ 73616 0 /* Default flags. */ 73617 }; 73618 73619 MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) 73620 { 73621 ma_result result; 73622 ma_node_config baseNodeConfig; 73623 73624 if (pNode == NULL) { 73625 return MA_INVALID_ARGS; 73626 } 73627 73628 MA_ZERO_OBJECT(pNode); 73629 73630 if (pConfig == NULL) { 73631 return MA_INVALID_ARGS; 73632 } 73633 73634 if (pConfig->peak.format != ma_format_f32) { 73635 return MA_INVALID_ARGS; /* The format must be f32. */ 73636 } 73637 73638 result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); 73639 if (result != MA_SUCCESS) { 73640 ma_node_uninit(pNode, pAllocationCallbacks); 73641 return result; 73642 } 73643 73644 baseNodeConfig = ma_node_config_init(); 73645 baseNodeConfig.vtable = &g_ma_peak_node_vtable; 73646 baseNodeConfig.pInputChannels = &pConfig->peak.channels; 73647 baseNodeConfig.pOutputChannels = &pConfig->peak.channels; 73648 73649 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73650 if (result != MA_SUCCESS) { 73651 return result; 73652 } 73653 73654 return result; 73655 } 73656 73657 MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) 73658 { 73659 ma_peak_node* pPeakNode = (ma_peak_node*)pNode; 73660 73661 if (pNode == NULL) { 73662 return MA_INVALID_ARGS; 73663 } 73664 73665 return ma_peak2_reinit(pConfig, &pPeakNode->peak); 73666 } 73667 73668 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73669 { 73670 ma_peak_node* pPeakNode = (ma_peak_node*)pNode; 73671 73672 if (pNode == NULL) { 73673 return; 73674 } 73675 73676 ma_node_uninit(pNode, pAllocationCallbacks); 73677 ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); 73678 } 73679 73680 73681 73682 /* 73683 Low Shelf Filter Node 73684 */ 73685 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) 73686 { 73687 ma_loshelf_node_config config; 73688 73689 config.nodeConfig = ma_node_config_init(); 73690 config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); 73691 73692 return config; 73693 } 73694 73695 static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73696 { 73697 ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; 73698 73699 MA_ASSERT(pNode != NULL); 73700 (void)pFrameCountIn; 73701 73702 ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73703 } 73704 73705 static ma_node_vtable g_ma_loshelf_node_vtable = 73706 { 73707 ma_loshelf_node_process_pcm_frames, 73708 NULL, /* onGetRequiredInputFrameCount */ 73709 1, /* One input. */ 73710 1, /* One output. */ 73711 0 /* Default flags. */ 73712 }; 73713 73714 MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) 73715 { 73716 ma_result result; 73717 ma_node_config baseNodeConfig; 73718 73719 if (pNode == NULL) { 73720 return MA_INVALID_ARGS; 73721 } 73722 73723 MA_ZERO_OBJECT(pNode); 73724 73725 if (pConfig == NULL) { 73726 return MA_INVALID_ARGS; 73727 } 73728 73729 if (pConfig->loshelf.format != ma_format_f32) { 73730 return MA_INVALID_ARGS; /* The format must be f32. */ 73731 } 73732 73733 result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); 73734 if (result != MA_SUCCESS) { 73735 return result; 73736 } 73737 73738 baseNodeConfig = ma_node_config_init(); 73739 baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; 73740 baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; 73741 baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; 73742 73743 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73744 if (result != MA_SUCCESS) { 73745 return result; 73746 } 73747 73748 return result; 73749 } 73750 73751 MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) 73752 { 73753 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; 73754 73755 if (pNode == NULL) { 73756 return MA_INVALID_ARGS; 73757 } 73758 73759 return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); 73760 } 73761 73762 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73763 { 73764 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; 73765 73766 if (pNode == NULL) { 73767 return; 73768 } 73769 73770 ma_node_uninit(pNode, pAllocationCallbacks); 73771 ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); 73772 } 73773 73774 73775 73776 /* 73777 High Shelf Filter Node 73778 */ 73779 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) 73780 { 73781 ma_hishelf_node_config config; 73782 73783 config.nodeConfig = ma_node_config_init(); 73784 config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); 73785 73786 return config; 73787 } 73788 73789 static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73790 { 73791 ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; 73792 73793 MA_ASSERT(pNode != NULL); 73794 (void)pFrameCountIn; 73795 73796 ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73797 } 73798 73799 static ma_node_vtable g_ma_hishelf_node_vtable = 73800 { 73801 ma_hishelf_node_process_pcm_frames, 73802 NULL, /* onGetRequiredInputFrameCount */ 73803 1, /* One input. */ 73804 1, /* One output. */ 73805 0 /* Default flags. */ 73806 }; 73807 73808 MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) 73809 { 73810 ma_result result; 73811 ma_node_config baseNodeConfig; 73812 73813 if (pNode == NULL) { 73814 return MA_INVALID_ARGS; 73815 } 73816 73817 MA_ZERO_OBJECT(pNode); 73818 73819 if (pConfig == NULL) { 73820 return MA_INVALID_ARGS; 73821 } 73822 73823 if (pConfig->hishelf.format != ma_format_f32) { 73824 return MA_INVALID_ARGS; /* The format must be f32. */ 73825 } 73826 73827 result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); 73828 if (result != MA_SUCCESS) { 73829 return result; 73830 } 73831 73832 baseNodeConfig = ma_node_config_init(); 73833 baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; 73834 baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; 73835 baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; 73836 73837 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); 73838 if (result != MA_SUCCESS) { 73839 return result; 73840 } 73841 73842 return result; 73843 } 73844 73845 MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) 73846 { 73847 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; 73848 73849 if (pNode == NULL) { 73850 return MA_INVALID_ARGS; 73851 } 73852 73853 return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); 73854 } 73855 73856 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) 73857 { 73858 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; 73859 73860 if (pNode == NULL) { 73861 return; 73862 } 73863 73864 ma_node_uninit(pNode, pAllocationCallbacks); 73865 ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); 73866 } 73867 73868 73869 73870 73871 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) 73872 { 73873 ma_delay_node_config config; 73874 73875 config.nodeConfig = ma_node_config_init(); 73876 config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); 73877 73878 return config; 73879 } 73880 73881 73882 static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 73883 { 73884 ma_delay_node* pDelayNode = (ma_delay_node*)pNode; 73885 73886 (void)pFrameCountIn; 73887 73888 ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); 73889 } 73890 73891 static ma_node_vtable g_ma_delay_node_vtable = 73892 { 73893 ma_delay_node_process_pcm_frames, 73894 NULL, 73895 1, /* 1 input channels. */ 73896 1, /* 1 output channel. */ 73897 MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ 73898 }; 73899 73900 MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) 73901 { 73902 ma_result result; 73903 ma_node_config baseConfig; 73904 73905 if (pDelayNode == NULL) { 73906 return MA_INVALID_ARGS; 73907 } 73908 73909 MA_ZERO_OBJECT(pDelayNode); 73910 73911 result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); 73912 if (result != MA_SUCCESS) { 73913 return result; 73914 } 73915 73916 baseConfig = pConfig->nodeConfig; 73917 baseConfig.vtable = &g_ma_delay_node_vtable; 73918 baseConfig.pInputChannels = &pConfig->delay.channels; 73919 baseConfig.pOutputChannels = &pConfig->delay.channels; 73920 73921 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); 73922 if (result != MA_SUCCESS) { 73923 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); 73924 return result; 73925 } 73926 73927 return result; 73928 } 73929 73930 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) 73931 { 73932 if (pDelayNode == NULL) { 73933 return; 73934 } 73935 73936 /* The base node is always uninitialized first. */ 73937 ma_node_uninit(pDelayNode, pAllocationCallbacks); 73938 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); 73939 } 73940 73941 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) 73942 { 73943 if (pDelayNode == NULL) { 73944 return; 73945 } 73946 73947 ma_delay_set_wet(&pDelayNode->delay, value); 73948 } 73949 73950 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) 73951 { 73952 if (pDelayNode == NULL) { 73953 return 0; 73954 } 73955 73956 return ma_delay_get_wet(&pDelayNode->delay); 73957 } 73958 73959 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) 73960 { 73961 if (pDelayNode == NULL) { 73962 return; 73963 } 73964 73965 ma_delay_set_dry(&pDelayNode->delay, value); 73966 } 73967 73968 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) 73969 { 73970 if (pDelayNode == NULL) { 73971 return 0; 73972 } 73973 73974 return ma_delay_get_dry(&pDelayNode->delay); 73975 } 73976 73977 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) 73978 { 73979 if (pDelayNode == NULL) { 73980 return; 73981 } 73982 73983 ma_delay_set_decay(&pDelayNode->delay, value); 73984 } 73985 73986 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) 73987 { 73988 if (pDelayNode == NULL) { 73989 return 0; 73990 } 73991 73992 return ma_delay_get_decay(&pDelayNode->delay); 73993 } 73994 #endif /* MA_NO_NODE_GRAPH */ 73995 73996 73997 /* SECTION: miniaudio_engine.c */ 73998 #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) 73999 /************************************************************************************************************************************************************** 74000 74001 Engine 74002 74003 **************************************************************************************************************************************************************/ 74004 #define MA_SEEK_TARGET_NONE (~(ma_uint64)0) 74005 74006 74007 static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) 74008 { 74009 MA_ASSERT(pSound != NULL); 74010 ma_atomic_exchange_32(&pSound->atEnd, atEnd); 74011 74012 /* Fire any callbacks or events. */ 74013 if (atEnd) { 74014 if (pSound->endCallback != NULL) { 74015 pSound->endCallback(pSound->pEndCallbackUserData, pSound); 74016 } 74017 } 74018 } 74019 74020 static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) 74021 { 74022 MA_ASSERT(pSound != NULL); 74023 return ma_atomic_load_32(&pSound->atEnd); 74024 } 74025 74026 74027 MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) 74028 { 74029 ma_engine_node_config config; 74030 74031 MA_ZERO_OBJECT(&config); 74032 config.pEngine = pEngine; 74033 config.type = type; 74034 config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; 74035 config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; 74036 config.monoExpansionMode = pEngine->monoExpansionMode; 74037 74038 return config; 74039 } 74040 74041 74042 static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) 74043 { 74044 ma_bool32 isUpdateRequired = MA_FALSE; 74045 float newPitch; 74046 74047 MA_ASSERT(pEngineNode != NULL); 74048 74049 newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); 74050 74051 if (pEngineNode->oldPitch != newPitch) { 74052 pEngineNode->oldPitch = newPitch; 74053 isUpdateRequired = MA_TRUE; 74054 } 74055 74056 if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { 74057 pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; 74058 isUpdateRequired = MA_TRUE; 74059 } 74060 74061 if (isUpdateRequired) { 74062 float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); 74063 ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); 74064 } 74065 } 74066 74067 static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) 74068 { 74069 MA_ASSERT(pEngineNode != NULL); 74070 74071 /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ 74072 return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); 74073 } 74074 74075 static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) 74076 { 74077 MA_ASSERT(pEngineNode != NULL); 74078 74079 return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); 74080 } 74081 74082 static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) 74083 { 74084 ma_uint64 inputFrameCount = 0; 74085 74086 if (ma_engine_node_is_pitching_enabled(pEngineNode)) { 74087 ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); 74088 if (result != MA_SUCCESS) { 74089 inputFrameCount = 0; 74090 } 74091 } else { 74092 inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ 74093 } 74094 74095 return inputFrameCount; 74096 } 74097 74098 static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) 74099 { 74100 if (pEngineNode == NULL) { 74101 return MA_INVALID_ARGS; 74102 } 74103 74104 ma_atomic_float_set(&pEngineNode->volume, volume); 74105 74106 /* If we're not smoothing we should bypass the volume gainer entirely. */ 74107 if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { 74108 /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ 74109 ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); 74110 } else { 74111 /* We're using volume smoothing, so apply the master volume to the gainer. */ 74112 ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); 74113 } 74114 74115 return MA_SUCCESS; 74116 } 74117 74118 static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) 74119 { 74120 if (pVolume == NULL) { 74121 return MA_INVALID_ARGS; 74122 } 74123 74124 *pVolume = 0.0f; 74125 74126 if (pEngineNode == NULL) { 74127 return MA_INVALID_ARGS; 74128 } 74129 74130 *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); 74131 74132 return MA_SUCCESS; 74133 } 74134 74135 74136 static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 74137 { 74138 ma_uint32 frameCountIn; 74139 ma_uint32 frameCountOut; 74140 ma_uint32 totalFramesProcessedIn; 74141 ma_uint32 totalFramesProcessedOut; 74142 ma_uint32 channelsIn; 74143 ma_uint32 channelsOut; 74144 ma_bool32 isPitchingEnabled; 74145 ma_bool32 isFadingEnabled; 74146 ma_bool32 isSpatializationEnabled; 74147 ma_bool32 isPanningEnabled; 74148 ma_bool32 isVolumeSmoothingEnabled; 74149 74150 frameCountIn = *pFrameCountIn; 74151 frameCountOut = *pFrameCountOut; 74152 74153 channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); 74154 channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); 74155 74156 totalFramesProcessedIn = 0; 74157 totalFramesProcessedOut = 0; 74158 74159 /* Update the fader if applicable. */ 74160 { 74161 ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); 74162 if (fadeLengthInFrames != ~(ma_uint64)0) { 74163 float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); 74164 float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); 74165 ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); 74166 if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { 74167 fadeStartOffsetInFrames = 0; 74168 } else { 74169 fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); 74170 } 74171 74172 ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); 74173 74174 /* Reset the fade length so we don't erroneously apply it again. */ 74175 ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); 74176 } 74177 } 74178 74179 isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); 74180 isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; 74181 isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); 74182 isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; 74183 isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; 74184 74185 /* Keep going while we've still got data available for processing. */ 74186 while (totalFramesProcessedOut < frameCountOut) { 74187 /* 74188 We need to process in a specific order. We always do resampling first because it's likely 74189 we're going to be increasing the channel count after spatialization. Also, I want to do 74190 fading based on the output sample rate. 74191 74192 We'll first read into a buffer from the resampler. Then we'll do all processing that 74193 operates on the on the input channel count. We'll then get the spatializer to output to 74194 the output buffer and then do all effects from that point directly in the output buffer 74195 in-place. 74196 74197 Note that we're always running the resampler if pitching is enabled, even when the pitch 74198 is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch 74199 when we move away from 1, back to 1, and then away from 1 again. We'll want to implement 74200 any pitch=1 optimizations in the resampler itself. 74201 74202 There's a small optimization here that we'll utilize since it might be a fairly common 74203 case. When the input and output channel counts are the same, we'll read straight into the 74204 output buffer from the resampler and do everything in-place. 74205 */ 74206 const float* pRunningFramesIn; 74207 float* pRunningFramesOut; 74208 float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ 74209 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; 74210 ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; 74211 ma_uint32 framesAvailableIn; 74212 ma_uint32 framesAvailableOut; 74213 ma_uint32 framesJustProcessedIn; 74214 ma_uint32 framesJustProcessedOut; 74215 ma_bool32 isWorkingBufferValid = MA_FALSE; 74216 74217 framesAvailableIn = frameCountIn - totalFramesProcessedIn; 74218 framesAvailableOut = frameCountOut - totalFramesProcessedOut; 74219 74220 pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); 74221 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); 74222 74223 if (channelsIn == channelsOut) { 74224 /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ 74225 pWorkingBuffer = pRunningFramesOut; 74226 } else { 74227 /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ 74228 pWorkingBuffer = temp; 74229 if (framesAvailableOut > tempCapInFrames) { 74230 framesAvailableOut = tempCapInFrames; 74231 } 74232 } 74233 74234 /* First is resampler. */ 74235 if (isPitchingEnabled) { 74236 ma_uint64 resampleFrameCountIn = framesAvailableIn; 74237 ma_uint64 resampleFrameCountOut = framesAvailableOut; 74238 74239 ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); 74240 isWorkingBufferValid = MA_TRUE; 74241 74242 framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; 74243 framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; 74244 } else { 74245 framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); 74246 framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ 74247 } 74248 74249 /* Fading. */ 74250 if (isFadingEnabled) { 74251 if (isWorkingBufferValid) { 74252 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ 74253 } else { 74254 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); 74255 isWorkingBufferValid = MA_TRUE; 74256 } 74257 } 74258 74259 /* 74260 If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case 74261 we'll want to apply our volume now. 74262 */ 74263 if (isVolumeSmoothingEnabled) { 74264 if (isWorkingBufferValid) { 74265 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); 74266 } else { 74267 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); 74268 isWorkingBufferValid = MA_TRUE; 74269 } 74270 } 74271 74272 /* 74273 If at this point we still haven't actually done anything with the working buffer we need 74274 to just read straight from the input buffer. 74275 */ 74276 if (isWorkingBufferValid == MA_FALSE) { 74277 pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ 74278 } 74279 74280 /* Spatialization. */ 74281 if (isSpatializationEnabled) { 74282 ma_uint32 iListener; 74283 74284 /* 74285 When determining the listener to use, we first check to see if the sound is pinned to a 74286 specific listener. If so, we use that. Otherwise we just use the closest listener. 74287 */ 74288 if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { 74289 iListener = pEngineNode->pinnedListenerIndex; 74290 } else { 74291 ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); 74292 iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); 74293 } 74294 74295 ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); 74296 } else { 74297 /* No spatialization, but we still need to do channel conversion and master volume. */ 74298 float volume; 74299 ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ 74300 74301 if (channelsIn == channelsOut) { 74302 /* No channel conversion required. Just copy straight to the output buffer. */ 74303 if (isVolumeSmoothingEnabled) { 74304 /* Volume has already been applied. Just copy straight to the output buffer. */ 74305 ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); 74306 } else { 74307 /* Volume has not been applied yet. Copy and apply volume in the same pass. */ 74308 ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); 74309 } 74310 } else { 74311 /* Channel conversion required. TODO: Add support for channel maps here. */ 74312 ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); 74313 74314 /* If we're using smoothing, the volume will have already been applied. */ 74315 if (!isVolumeSmoothingEnabled) { 74316 ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); 74317 } 74318 } 74319 } 74320 74321 /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ 74322 74323 /* Panning. */ 74324 if (isPanningEnabled) { 74325 ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ 74326 } 74327 74328 /* We're done for this chunk. */ 74329 totalFramesProcessedIn += framesJustProcessedIn; 74330 totalFramesProcessedOut += framesJustProcessedOut; 74331 74332 /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ 74333 if (framesJustProcessedOut == 0) { 74334 break; 74335 } 74336 } 74337 74338 /* At this point we're done processing. */ 74339 *pFrameCountIn = totalFramesProcessedIn; 74340 *pFrameCountOut = totalFramesProcessedOut; 74341 } 74342 74343 static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 74344 { 74345 /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ 74346 ma_result result = MA_SUCCESS; 74347 ma_sound* pSound = (ma_sound*)pNode; 74348 ma_uint32 frameCount = *pFrameCountOut; 74349 ma_uint32 totalFramesRead = 0; 74350 ma_format dataSourceFormat; 74351 ma_uint32 dataSourceChannels; 74352 ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; 74353 ma_uint32 tempCapInFrames; 74354 ma_uint64 seekTarget; 74355 74356 /* This is a data source node which means no input buses. */ 74357 (void)ppFramesIn; 74358 (void)pFrameCountIn; 74359 74360 /* If we're marked at the end we need to stop the sound and do nothing. */ 74361 if (ma_sound_at_end(pSound)) { 74362 ma_sound_stop(pSound); 74363 *pFrameCountOut = 0; 74364 return; 74365 } 74366 74367 /* If we're seeking, do so now before reading. */ 74368 seekTarget = ma_atomic_load_64(&pSound->seekTarget); 74369 if (seekTarget != MA_SEEK_TARGET_NONE) { 74370 ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); 74371 74372 /* Any time-dependant effects need to have their times updated. */ 74373 ma_node_set_time(pSound, seekTarget); 74374 74375 ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); 74376 } 74377 74378 /* 74379 We want to update the pitch once. For sounds, this can be either at the start or at the end. If 74380 we don't force this to only ever be updating once, we could end up in a situation where 74381 retrieving the required input frame count ends up being different to what we actually retrieve. 74382 What could happen is that the required input frame count is calculated, the pitch is update, 74383 and then this processing function is called resulting in a different number of input frames 74384 being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else 74385 you'll hit the aforementioned bug. 74386 */ 74387 ma_engine_node_update_pitch_if_required(&pSound->engineNode); 74388 74389 /* 74390 For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ 74391 from the main engine. 74392 */ 74393 result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); 74394 if (result == MA_SUCCESS) { 74395 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); 74396 74397 /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ 74398 while (totalFramesRead < frameCount) { 74399 ma_uint32 framesRemaining = frameCount - totalFramesRead; 74400 ma_uint32 framesToRead; 74401 ma_uint64 framesJustRead; 74402 ma_uint32 frameCountIn; 74403 ma_uint32 frameCountOut; 74404 const float* pRunningFramesIn; 74405 float* pRunningFramesOut; 74406 74407 /* 74408 The first thing we need to do is read into the temporary buffer. We can calculate exactly 74409 how many input frames we'll need after resampling. 74410 */ 74411 framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); 74412 if (framesToRead > tempCapInFrames) { 74413 framesToRead = tempCapInFrames; 74414 } 74415 74416 result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); 74417 74418 /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ 74419 if (result == MA_AT_END) { 74420 ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ 74421 } 74422 74423 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); 74424 74425 frameCountIn = (ma_uint32)framesJustRead; 74426 frameCountOut = framesRemaining; 74427 74428 /* Convert if necessary. */ 74429 if (dataSourceFormat == ma_format_f32) { 74430 /* Fast path. No data conversion necessary. */ 74431 pRunningFramesIn = (float*)temp; 74432 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); 74433 } else { 74434 /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ 74435 float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ 74436 ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); 74437 74438 /* Now that we have our samples in f32 format we can process like normal. */ 74439 pRunningFramesIn = tempf32; 74440 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); 74441 } 74442 74443 /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ 74444 MA_ASSERT(frameCountIn == framesJustRead); 74445 totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ 74446 74447 if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { 74448 break; /* Might have reached the end. */ 74449 } 74450 } 74451 } 74452 74453 *pFrameCountOut = totalFramesRead; 74454 } 74455 74456 static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) 74457 { 74458 /* 74459 Make sure the pitch is updated before trying to read anything. It's important that this is done 74460 only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that 74461 ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), 74462 and if another thread modifies the pitch just after that call it can result in a glitch due to 74463 the input rate changing. 74464 */ 74465 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); 74466 74467 /* For groups, the input data has already been read and we just need to apply the effect. */ 74468 ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); 74469 } 74470 74471 static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) 74472 { 74473 ma_uint64 inputFrameCount; 74474 74475 MA_ASSERT(pInputFrameCount != NULL); 74476 74477 /* Our pitch will affect this calculation. We need to update it. */ 74478 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); 74479 74480 inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); 74481 if (inputFrameCount > 0xFFFFFFFF) { 74482 inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ 74483 } 74484 74485 *pInputFrameCount = (ma_uint32)inputFrameCount; 74486 74487 return MA_SUCCESS; 74488 } 74489 74490 74491 static ma_node_vtable g_ma_engine_node_vtable__sound = 74492 { 74493 ma_engine_node_process_pcm_frames__sound, 74494 NULL, /* onGetRequiredInputFrameCount */ 74495 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ 74496 1, /* Sounds have one output bus. */ 74497 0 /* Default flags. */ 74498 }; 74499 74500 static ma_node_vtable g_ma_engine_node_vtable__group = 74501 { 74502 ma_engine_node_process_pcm_frames__group, 74503 ma_engine_node_get_required_input_frame_count__group, 74504 1, /* Groups have one input bus. */ 74505 1, /* Groups have one output bus. */ 74506 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ 74507 }; 74508 74509 74510 74511 static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) 74512 { 74513 ma_node_config baseNodeConfig; 74514 74515 if (pConfig->type == ma_engine_node_type_sound) { 74516 /* Sound. */ 74517 baseNodeConfig = ma_node_config_init(); 74518 baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; 74519 baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ 74520 } else { 74521 /* Group. */ 74522 baseNodeConfig = ma_node_config_init(); 74523 baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; 74524 baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ 74525 } 74526 74527 return baseNodeConfig; 74528 } 74529 74530 static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) 74531 { 74532 return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); 74533 } 74534 74535 typedef struct 74536 { 74537 size_t sizeInBytes; 74538 size_t baseNodeOffset; 74539 size_t resamplerOffset; 74540 size_t spatializerOffset; 74541 size_t gainerOffset; 74542 } ma_engine_node_heap_layout; 74543 74544 static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) 74545 { 74546 ma_result result; 74547 size_t tempHeapSize; 74548 ma_node_config baseNodeConfig; 74549 ma_linear_resampler_config resamplerConfig; 74550 ma_spatializer_config spatializerConfig; 74551 ma_gainer_config gainerConfig; 74552 ma_uint32 channelsIn; 74553 ma_uint32 channelsOut; 74554 ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ 74555 74556 MA_ASSERT(pHeapLayout); 74557 74558 MA_ZERO_OBJECT(pHeapLayout); 74559 74560 if (pConfig == NULL) { 74561 return MA_INVALID_ARGS; 74562 } 74563 74564 if (pConfig->pEngine == NULL) { 74565 return MA_INVALID_ARGS; /* An engine must be specified. */ 74566 } 74567 74568 pHeapLayout->sizeInBytes = 0; 74569 74570 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); 74571 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); 74572 74573 74574 /* Base node. */ 74575 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); 74576 baseNodeConfig.pInputChannels = &channelsIn; 74577 baseNodeConfig.pOutputChannels = &channelsOut; 74578 74579 result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); 74580 if (result != MA_SUCCESS) { 74581 return result; /* Failed to retrieve the size of the heap for the base node. */ 74582 } 74583 74584 pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; 74585 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); 74586 74587 74588 /* Resmapler. */ 74589 resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ 74590 resamplerConfig.lpfOrder = 0; 74591 74592 result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); 74593 if (result != MA_SUCCESS) { 74594 return result; /* Failed to retrieve the size of the heap for the resampler. */ 74595 } 74596 74597 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; 74598 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); 74599 74600 74601 /* Spatializer. */ 74602 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); 74603 74604 if (spatializerConfig.channelsIn == 2) { 74605 spatializerConfig.pChannelMapIn = defaultStereoChannelMap; 74606 } 74607 74608 result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); 74609 if (result != MA_SUCCESS) { 74610 return result; /* Failed to retrieve the size of the heap for the spatializer. */ 74611 } 74612 74613 pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; 74614 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); 74615 74616 74617 /* Gainer. Will not be used if we are not using smoothing. */ 74618 if (pConfig->volumeSmoothTimeInPCMFrames > 0) { 74619 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); 74620 74621 result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); 74622 if (result != MA_SUCCESS) { 74623 return result; 74624 } 74625 74626 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; 74627 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); 74628 } 74629 74630 74631 return MA_SUCCESS; 74632 } 74633 74634 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) 74635 { 74636 ma_result result; 74637 ma_engine_node_heap_layout heapLayout; 74638 74639 if (pHeapSizeInBytes == NULL) { 74640 return MA_INVALID_ARGS; 74641 } 74642 74643 *pHeapSizeInBytes = 0; 74644 74645 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); 74646 if (result != MA_SUCCESS) { 74647 return result; 74648 } 74649 74650 *pHeapSizeInBytes = heapLayout.sizeInBytes; 74651 74652 return MA_SUCCESS; 74653 } 74654 74655 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) 74656 { 74657 ma_result result; 74658 ma_engine_node_heap_layout heapLayout; 74659 ma_node_config baseNodeConfig; 74660 ma_linear_resampler_config resamplerConfig; 74661 ma_fader_config faderConfig; 74662 ma_spatializer_config spatializerConfig; 74663 ma_panner_config pannerConfig; 74664 ma_gainer_config gainerConfig; 74665 ma_uint32 channelsIn; 74666 ma_uint32 channelsOut; 74667 ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ 74668 74669 if (pEngineNode == NULL) { 74670 return MA_INVALID_ARGS; 74671 } 74672 74673 MA_ZERO_OBJECT(pEngineNode); 74674 74675 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); 74676 if (result != MA_SUCCESS) { 74677 return result; 74678 } 74679 74680 if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { 74681 return MA_INVALID_ARGS; /* Invalid listener. */ 74682 } 74683 74684 pEngineNode->_pHeap = pHeap; 74685 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); 74686 74687 pEngineNode->pEngine = pConfig->pEngine; 74688 pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); 74689 pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; 74690 pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; 74691 ma_atomic_float_set(&pEngineNode->volume, 1); 74692 pEngineNode->pitch = 1; 74693 pEngineNode->oldPitch = 1; 74694 pEngineNode->oldDopplerPitch = 1; 74695 pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; 74696 pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; 74697 pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; 74698 ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); 74699 ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); 74700 ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); 74701 ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ 74702 74703 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); 74704 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); 74705 74706 /* 74707 If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler 74708 is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. 74709 */ 74710 if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { 74711 pEngineNode->isPitchDisabled = MA_FALSE; 74712 } 74713 74714 74715 /* Base node. */ 74716 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); 74717 baseNodeConfig.pInputChannels = &channelsIn; 74718 baseNodeConfig.pOutputChannels = &channelsOut; 74719 74720 result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); 74721 if (result != MA_SUCCESS) { 74722 goto error0; 74723 } 74724 74725 74726 /* 74727 We can now initialize the effects we need in order to implement the engine node. There's a 74728 defined order of operations here, mainly centered around when we convert our channels from the 74729 data source's native channel count to the engine's channel count. As a rule, we want to do as 74730 much computation as possible before spatialization because there's a chance that will increase 74731 the channel count, thereby increasing the amount of work needing to be done to process. 74732 */ 74733 74734 /* We'll always do resampling first. */ 74735 resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); 74736 resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ 74737 74738 result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); 74739 if (result != MA_SUCCESS) { 74740 goto error1; 74741 } 74742 74743 74744 /* After resampling will come the fader. */ 74745 faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); 74746 74747 result = ma_fader_init(&faderConfig, &pEngineNode->fader); 74748 if (result != MA_SUCCESS) { 74749 goto error2; 74750 } 74751 74752 74753 /* 74754 Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to 74755 ensure channels counts link up correctly in the node graph. 74756 */ 74757 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); 74758 spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; 74759 74760 if (spatializerConfig.channelsIn == 2) { 74761 spatializerConfig.pChannelMapIn = defaultStereoChannelMap; 74762 } 74763 74764 result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); 74765 if (result != MA_SUCCESS) { 74766 goto error2; 74767 } 74768 74769 74770 /* 74771 After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't 74772 be able to pan mono sounds. 74773 */ 74774 pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); 74775 74776 result = ma_panner_init(&pannerConfig, &pEngineNode->panner); 74777 if (result != MA_SUCCESS) { 74778 goto error3; 74779 } 74780 74781 74782 /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ 74783 if (pConfig->volumeSmoothTimeInPCMFrames > 0) { 74784 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); 74785 74786 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); 74787 if (result != MA_SUCCESS) { 74788 goto error3; 74789 } 74790 } 74791 74792 74793 return MA_SUCCESS; 74794 74795 /* No need for allocation callbacks here because we use a preallocated heap. */ 74796 error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); 74797 error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); 74798 error1: ma_node_uninit(&pEngineNode->baseNode, NULL); 74799 error0: return result; 74800 } 74801 74802 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) 74803 { 74804 ma_result result; 74805 size_t heapSizeInBytes; 74806 void* pHeap; 74807 74808 result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); 74809 if (result != MA_SUCCESS) { 74810 return result; 74811 } 74812 74813 if (heapSizeInBytes > 0) { 74814 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); 74815 if (pHeap == NULL) { 74816 return MA_OUT_OF_MEMORY; 74817 } 74818 } else { 74819 pHeap = NULL; 74820 } 74821 74822 result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); 74823 if (result != MA_SUCCESS) { 74824 ma_free(pHeap, pAllocationCallbacks); 74825 return result; 74826 } 74827 74828 pEngineNode->_ownsHeap = MA_TRUE; 74829 return MA_SUCCESS; 74830 } 74831 74832 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) 74833 { 74834 /* 74835 The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we 74836 destroy anything that might be in the middle of being used by the processing function. 74837 */ 74838 ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); 74839 74840 /* Now that the node has been uninitialized we can safely uninitialize the rest. */ 74841 if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { 74842 ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); 74843 } 74844 74845 ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); 74846 ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); 74847 74848 /* Free the heap last. */ 74849 if (pEngineNode->_ownsHeap) { 74850 ma_free(pEngineNode->_pHeap, pAllocationCallbacks); 74851 } 74852 } 74853 74854 74855 MA_API ma_sound_config ma_sound_config_init(void) 74856 { 74857 return ma_sound_config_init_2(NULL); 74858 } 74859 74860 MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) 74861 { 74862 ma_sound_config config; 74863 74864 MA_ZERO_OBJECT(&config); 74865 74866 if (pEngine != NULL) { 74867 config.monoExpansionMode = pEngine->monoExpansionMode; 74868 } else { 74869 config.monoExpansionMode = ma_mono_expansion_mode_default; 74870 } 74871 74872 config.rangeEndInPCMFrames = ~((ma_uint64)0); 74873 config.loopPointEndInPCMFrames = ~((ma_uint64)0); 74874 74875 return config; 74876 } 74877 74878 MA_API ma_sound_group_config ma_sound_group_config_init(void) 74879 { 74880 return ma_sound_group_config_init_2(NULL); 74881 } 74882 74883 MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) 74884 { 74885 ma_sound_group_config config; 74886 74887 MA_ZERO_OBJECT(&config); 74888 74889 if (pEngine != NULL) { 74890 config.monoExpansionMode = pEngine->monoExpansionMode; 74891 } else { 74892 config.monoExpansionMode = ma_mono_expansion_mode_default; 74893 } 74894 74895 return config; 74896 } 74897 74898 74899 MA_API ma_engine_config ma_engine_config_init(void) 74900 { 74901 ma_engine_config config; 74902 74903 MA_ZERO_OBJECT(&config); 74904 config.listenerCount = 1; /* Always want at least one listener. */ 74905 config.monoExpansionMode = ma_mono_expansion_mode_default; 74906 74907 return config; 74908 } 74909 74910 74911 #if !defined(MA_NO_DEVICE_IO) 74912 static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) 74913 { 74914 ma_engine* pEngine = (ma_engine*)pDevice->pUserData; 74915 74916 (void)pFramesIn; 74917 74918 /* 74919 Experiment: Try processing a resource manager job if we're on the Emscripten build. 74920 74921 This serves two purposes: 74922 74923 1) It ensures jobs are actually processed at some point since we cannot guarantee that the 74924 caller is doing the right thing and calling ma_resource_manager_process_next_job(); and 74925 74926 2) It's an attempt at working around an issue where processing jobs on the Emscripten main 74927 loop doesn't work as well as it should. When trying to load sounds without the `DECODE` 74928 flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time 74929 before the callback is processed. I think it's got something to do with the single- 74930 threaded nature of Web, but I'm not entirely sure. 74931 */ 74932 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) 74933 { 74934 if (pEngine->pResourceManager != NULL) { 74935 if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { 74936 ma_resource_manager_process_next_job(pEngine->pResourceManager); 74937 } 74938 } 74939 } 74940 #endif 74941 74942 ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); 74943 } 74944 #endif 74945 74946 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) 74947 { 74948 ma_result result; 74949 ma_node_graph_config nodeGraphConfig; 74950 ma_engine_config engineConfig; 74951 ma_spatializer_listener_config listenerConfig; 74952 ma_uint32 iListener; 74953 74954 if (pEngine == NULL) { 74955 return MA_INVALID_ARGS; 74956 } 74957 74958 MA_ZERO_OBJECT(pEngine); 74959 74960 /* The config is allowed to be NULL in which case we use defaults for everything. */ 74961 if (pConfig != NULL) { 74962 engineConfig = *pConfig; 74963 } else { 74964 engineConfig = ma_engine_config_init(); 74965 } 74966 74967 pEngine->monoExpansionMode = engineConfig.monoExpansionMode; 74968 pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; 74969 pEngine->onProcess = engineConfig.onProcess; 74970 pEngine->pProcessUserData = engineConfig.pProcessUserData; 74971 ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); 74972 74973 #if !defined(MA_NO_RESOURCE_MANAGER) 74974 { 74975 pEngine->pResourceManager = engineConfig.pResourceManager; 74976 } 74977 #endif 74978 74979 #if !defined(MA_NO_DEVICE_IO) 74980 { 74981 pEngine->pDevice = engineConfig.pDevice; 74982 74983 /* If we don't have a device, we need one. */ 74984 if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { 74985 ma_device_config deviceConfig; 74986 74987 pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); 74988 if (pEngine->pDevice == NULL) { 74989 return MA_OUT_OF_MEMORY; 74990 } 74991 74992 deviceConfig = ma_device_config_init(ma_device_type_playback); 74993 deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; 74994 deviceConfig.playback.format = ma_format_f32; 74995 deviceConfig.playback.channels = engineConfig.channels; 74996 deviceConfig.sampleRate = engineConfig.sampleRate; 74997 deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; 74998 deviceConfig.pUserData = pEngine; 74999 deviceConfig.notificationCallback = engineConfig.notificationCallback; 75000 deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; 75001 deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; 75002 deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ 75003 deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ 75004 75005 if (engineConfig.pContext == NULL) { 75006 ma_context_config contextConfig = ma_context_config_init(); 75007 contextConfig.allocationCallbacks = pEngine->allocationCallbacks; 75008 contextConfig.pLog = engineConfig.pLog; 75009 75010 /* If the engine config does not specify a log, use the resource manager's if we have one. */ 75011 #ifndef MA_NO_RESOURCE_MANAGER 75012 { 75013 if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { 75014 contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); 75015 } 75016 } 75017 #endif 75018 75019 result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); 75020 } else { 75021 result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); 75022 } 75023 75024 if (result != MA_SUCCESS) { 75025 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); 75026 pEngine->pDevice = NULL; 75027 return result; 75028 } 75029 75030 pEngine->ownsDevice = MA_TRUE; 75031 } 75032 75033 /* Update the channel count and sample rate of the engine config so we can reference it below. */ 75034 if (pEngine->pDevice != NULL) { 75035 engineConfig.channels = pEngine->pDevice->playback.channels; 75036 engineConfig.sampleRate = pEngine->pDevice->sampleRate; 75037 } 75038 } 75039 #endif 75040 75041 if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { 75042 return MA_INVALID_ARGS; 75043 } 75044 75045 pEngine->sampleRate = engineConfig.sampleRate; 75046 75047 /* The engine always uses either the log that was passed into the config, or the context's log is available. */ 75048 if (engineConfig.pLog != NULL) { 75049 pEngine->pLog = engineConfig.pLog; 75050 } else { 75051 #if !defined(MA_NO_DEVICE_IO) 75052 { 75053 pEngine->pLog = ma_device_get_log(pEngine->pDevice); 75054 } 75055 #else 75056 { 75057 pEngine->pLog = NULL; 75058 } 75059 #endif 75060 } 75061 75062 75063 /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ 75064 nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); 75065 nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; 75066 75067 result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); 75068 if (result != MA_SUCCESS) { 75069 goto on_error_1; 75070 } 75071 75072 75073 /* We need at least one listener. */ 75074 if (engineConfig.listenerCount == 0) { 75075 engineConfig.listenerCount = 1; 75076 } 75077 75078 if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { 75079 result = MA_INVALID_ARGS; /* Too many listeners. */ 75080 goto on_error_1; 75081 } 75082 75083 for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { 75084 listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); 75085 75086 /* 75087 If we're using a device, use the device's channel map for the listener. Otherwise just use 75088 miniaudio's default channel map. 75089 */ 75090 #if !defined(MA_NO_DEVICE_IO) 75091 { 75092 if (pEngine->pDevice != NULL) { 75093 /* 75094 Temporarily disabled. There is a subtle bug here where front-left and front-right 75095 will be used by the device's channel map, but this is not what we want to use for 75096 spatialization. Instead we want to use side-left and side-right. I need to figure 75097 out a better solution for this. For now, disabling the use of device channel maps. 75098 */ 75099 /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ 75100 } 75101 } 75102 #endif 75103 75104 result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ 75105 if (result != MA_SUCCESS) { 75106 goto on_error_2; 75107 } 75108 75109 pEngine->listenerCount += 1; 75110 } 75111 75112 75113 /* Gain smoothing for spatialized sounds. */ 75114 pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; 75115 if (pEngine->gainSmoothTimeInFrames == 0) { 75116 ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; 75117 if (gainSmoothTimeInMilliseconds == 0) { 75118 gainSmoothTimeInMilliseconds = 8; 75119 } 75120 75121 pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ 75122 } 75123 75124 75125 /* We need a resource manager. */ 75126 #ifndef MA_NO_RESOURCE_MANAGER 75127 { 75128 if (pEngine->pResourceManager == NULL) { 75129 ma_resource_manager_config resourceManagerConfig; 75130 75131 pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); 75132 if (pEngine->pResourceManager == NULL) { 75133 result = MA_OUT_OF_MEMORY; 75134 goto on_error_2; 75135 } 75136 75137 resourceManagerConfig = ma_resource_manager_config_init(); 75138 resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ 75139 resourceManagerConfig.decodedFormat = ma_format_f32; 75140 resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ 75141 resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); 75142 ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); 75143 resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; 75144 75145 /* The Emscripten build cannot use threads. */ 75146 #if defined(MA_EMSCRIPTEN) 75147 { 75148 resourceManagerConfig.jobThreadCount = 0; 75149 resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; 75150 } 75151 #endif 75152 75153 result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); 75154 if (result != MA_SUCCESS) { 75155 goto on_error_3; 75156 } 75157 75158 pEngine->ownsResourceManager = MA_TRUE; 75159 } 75160 } 75161 #endif 75162 75163 /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ 75164 pEngine->inlinedSoundLock = 0; 75165 pEngine->pInlinedSoundHead = NULL; 75166 75167 /* Start the engine if required. This should always be the last step. */ 75168 #if !defined(MA_NO_DEVICE_IO) 75169 { 75170 if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { 75171 result = ma_engine_start(pEngine); 75172 if (result != MA_SUCCESS) { 75173 goto on_error_4; /* Failed to start the engine. */ 75174 } 75175 } 75176 } 75177 #endif 75178 75179 return MA_SUCCESS; 75180 75181 #if !defined(MA_NO_DEVICE_IO) 75182 on_error_4: 75183 #endif 75184 #if !defined(MA_NO_RESOURCE_MANAGER) 75185 on_error_3: 75186 if (pEngine->ownsResourceManager) { 75187 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); 75188 } 75189 #endif /* MA_NO_RESOURCE_MANAGER */ 75190 on_error_2: 75191 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { 75192 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); 75193 } 75194 75195 ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); 75196 on_error_1: 75197 #if !defined(MA_NO_DEVICE_IO) 75198 { 75199 if (pEngine->ownsDevice) { 75200 ma_device_uninit(pEngine->pDevice); 75201 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); 75202 } 75203 } 75204 #endif 75205 75206 return result; 75207 } 75208 75209 MA_API void ma_engine_uninit(ma_engine* pEngine) 75210 { 75211 ma_uint32 iListener; 75212 75213 if (pEngine == NULL) { 75214 return; 75215 } 75216 75217 /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ 75218 #if !defined(MA_NO_DEVICE_IO) 75219 { 75220 if (pEngine->ownsDevice) { 75221 ma_device_uninit(pEngine->pDevice); 75222 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); 75223 } else { 75224 if (pEngine->pDevice != NULL) { 75225 ma_device_stop(pEngine->pDevice); 75226 } 75227 } 75228 } 75229 #endif 75230 75231 /* 75232 All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case 75233 I want to do some kind of garbage collection later on. 75234 */ 75235 ma_spinlock_lock(&pEngine->inlinedSoundLock); 75236 { 75237 for (;;) { 75238 ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; 75239 if (pSoundToDelete == NULL) { 75240 break; /* Done. */ 75241 } 75242 75243 pEngine->pInlinedSoundHead = pSoundToDelete->pNext; 75244 75245 ma_sound_uninit(&pSoundToDelete->sound); 75246 ma_free(pSoundToDelete, &pEngine->allocationCallbacks); 75247 } 75248 } 75249 ma_spinlock_unlock(&pEngine->inlinedSoundLock); 75250 75251 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { 75252 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); 75253 } 75254 75255 /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ 75256 ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); 75257 75258 /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ 75259 #ifndef MA_NO_RESOURCE_MANAGER 75260 if (pEngine->ownsResourceManager) { 75261 ma_resource_manager_uninit(pEngine->pResourceManager); 75262 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); 75263 } 75264 #endif 75265 } 75266 75267 MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) 75268 { 75269 ma_result result; 75270 ma_uint64 framesRead = 0; 75271 75272 if (pFramesRead != NULL) { 75273 *pFramesRead = 0; 75274 } 75275 75276 result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); 75277 if (result != MA_SUCCESS) { 75278 return result; 75279 } 75280 75281 if (pFramesRead != NULL) { 75282 *pFramesRead = framesRead; 75283 } 75284 75285 if (pEngine->onProcess) { 75286 pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ 75287 } 75288 75289 return MA_SUCCESS; 75290 } 75291 75292 MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) 75293 { 75294 if (pEngine == NULL) { 75295 return NULL; 75296 } 75297 75298 return &pEngine->nodeGraph; 75299 } 75300 75301 #if !defined(MA_NO_RESOURCE_MANAGER) 75302 MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) 75303 { 75304 if (pEngine == NULL) { 75305 return NULL; 75306 } 75307 75308 #if !defined(MA_NO_RESOURCE_MANAGER) 75309 { 75310 return pEngine->pResourceManager; 75311 } 75312 #else 75313 { 75314 return NULL; 75315 } 75316 #endif 75317 } 75318 #endif 75319 75320 MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) 75321 { 75322 if (pEngine == NULL) { 75323 return NULL; 75324 } 75325 75326 #if !defined(MA_NO_DEVICE_IO) 75327 { 75328 return pEngine->pDevice; 75329 } 75330 #else 75331 { 75332 return NULL; 75333 } 75334 #endif 75335 } 75336 75337 MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) 75338 { 75339 if (pEngine == NULL) { 75340 return NULL; 75341 } 75342 75343 if (pEngine->pLog != NULL) { 75344 return pEngine->pLog; 75345 } else { 75346 #if !defined(MA_NO_DEVICE_IO) 75347 { 75348 return ma_device_get_log(ma_engine_get_device(pEngine)); 75349 } 75350 #else 75351 { 75352 return NULL; 75353 } 75354 #endif 75355 } 75356 } 75357 75358 MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) 75359 { 75360 return ma_node_graph_get_endpoint(&pEngine->nodeGraph); 75361 } 75362 75363 MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) 75364 { 75365 return ma_node_graph_get_time(&pEngine->nodeGraph); 75366 } 75367 75368 MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) 75369 { 75370 return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); 75371 } 75372 75373 MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) 75374 { 75375 return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); 75376 } 75377 75378 MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) 75379 { 75380 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); 75381 } 75382 75383 MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) 75384 { 75385 return ma_engine_get_time_in_pcm_frames(pEngine); 75386 } 75387 75388 MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) 75389 { 75390 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); 75391 } 75392 75393 MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) 75394 { 75395 return ma_node_graph_get_channels(&pEngine->nodeGraph); 75396 } 75397 75398 MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) 75399 { 75400 if (pEngine == NULL) { 75401 return 0; 75402 } 75403 75404 return pEngine->sampleRate; 75405 } 75406 75407 75408 MA_API ma_result ma_engine_start(ma_engine* pEngine) 75409 { 75410 ma_result result; 75411 75412 if (pEngine == NULL) { 75413 return MA_INVALID_ARGS; 75414 } 75415 75416 #if !defined(MA_NO_DEVICE_IO) 75417 { 75418 if (pEngine->pDevice != NULL) { 75419 result = ma_device_start(pEngine->pDevice); 75420 } else { 75421 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ 75422 } 75423 } 75424 #else 75425 { 75426 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ 75427 } 75428 #endif 75429 75430 if (result != MA_SUCCESS) { 75431 return result; 75432 } 75433 75434 return MA_SUCCESS; 75435 } 75436 75437 MA_API ma_result ma_engine_stop(ma_engine* pEngine) 75438 { 75439 ma_result result; 75440 75441 if (pEngine == NULL) { 75442 return MA_INVALID_ARGS; 75443 } 75444 75445 #if !defined(MA_NO_DEVICE_IO) 75446 { 75447 if (pEngine->pDevice != NULL) { 75448 result = ma_device_stop(pEngine->pDevice); 75449 } else { 75450 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ 75451 } 75452 } 75453 #else 75454 { 75455 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ 75456 } 75457 #endif 75458 75459 if (result != MA_SUCCESS) { 75460 return result; 75461 } 75462 75463 return MA_SUCCESS; 75464 } 75465 75466 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) 75467 { 75468 if (pEngine == NULL) { 75469 return MA_INVALID_ARGS; 75470 } 75471 75472 return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); 75473 } 75474 75475 MA_API float ma_engine_get_volume(ma_engine* pEngine) 75476 { 75477 if (pEngine == NULL) { 75478 return 0; 75479 } 75480 75481 return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); 75482 } 75483 75484 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) 75485 { 75486 return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); 75487 } 75488 75489 MA_API float ma_engine_get_gain_db(ma_engine* pEngine) 75490 { 75491 return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); 75492 } 75493 75494 75495 MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) 75496 { 75497 if (pEngine == NULL) { 75498 return 0; 75499 } 75500 75501 return pEngine->listenerCount; 75502 } 75503 75504 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) 75505 { 75506 ma_uint32 iListener; 75507 ma_uint32 iListenerClosest; 75508 float closestLen2 = MA_FLT_MAX; 75509 75510 if (pEngine == NULL || pEngine->listenerCount == 1) { 75511 return 0; 75512 } 75513 75514 iListenerClosest = 0; 75515 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { 75516 if (ma_engine_listener_is_enabled(pEngine, iListener)) { 75517 float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); 75518 if (closestLen2 > len2) { 75519 closestLen2 = len2; 75520 iListenerClosest = iListener; 75521 } 75522 } 75523 } 75524 75525 MA_ASSERT(iListenerClosest < 255); 75526 return iListenerClosest; 75527 } 75528 75529 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) 75530 { 75531 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75532 return; 75533 } 75534 75535 ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); 75536 } 75537 75538 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) 75539 { 75540 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75541 return ma_vec3f_init_3f(0, 0, 0); 75542 } 75543 75544 return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); 75545 } 75546 75547 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) 75548 { 75549 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75550 return; 75551 } 75552 75553 ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); 75554 } 75555 75556 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) 75557 { 75558 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75559 return ma_vec3f_init_3f(0, 0, -1); 75560 } 75561 75562 return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); 75563 } 75564 75565 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) 75566 { 75567 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75568 return; 75569 } 75570 75571 ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); 75572 } 75573 75574 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) 75575 { 75576 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75577 return ma_vec3f_init_3f(0, 0, 0); 75578 } 75579 75580 return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); 75581 } 75582 75583 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) 75584 { 75585 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75586 return; 75587 } 75588 75589 ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); 75590 } 75591 75592 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) 75593 { 75594 if (pInnerAngleInRadians != NULL) { 75595 *pInnerAngleInRadians = 0; 75596 } 75597 75598 if (pOuterAngleInRadians != NULL) { 75599 *pOuterAngleInRadians = 0; 75600 } 75601 75602 if (pOuterGain != NULL) { 75603 *pOuterGain = 0; 75604 } 75605 75606 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75607 return; 75608 } 75609 75610 ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); 75611 } 75612 75613 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) 75614 { 75615 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75616 return; 75617 } 75618 75619 ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); 75620 } 75621 75622 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) 75623 { 75624 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75625 return ma_vec3f_init_3f(0, 1, 0); 75626 } 75627 75628 return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); 75629 } 75630 75631 MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) 75632 { 75633 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75634 return; 75635 } 75636 75637 ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); 75638 } 75639 75640 MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) 75641 { 75642 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { 75643 return MA_FALSE; 75644 } 75645 75646 return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); 75647 } 75648 75649 75650 #ifndef MA_NO_RESOURCE_MANAGER 75651 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) 75652 { 75653 ma_result result = MA_SUCCESS; 75654 ma_sound_inlined* pSound = NULL; 75655 ma_sound_inlined* pNextSound = NULL; 75656 75657 if (pEngine == NULL || pFilePath == NULL) { 75658 return MA_INVALID_ARGS; 75659 } 75660 75661 /* Attach to the endpoint node if nothing is specicied. */ 75662 if (pNode == NULL) { 75663 pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); 75664 nodeInputBusIndex = 0; 75665 } 75666 75667 /* 75668 We want to check if we can recycle an already-allocated inlined sound. Since this is just a 75669 helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep 75670 the implementation simple. Maybe this can be optimized later if there's enough demand, but 75671 if this function is being used it probably means the caller doesn't really care too much. 75672 75673 What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise 75674 we just keep iterating. If we reach the end without finding a sound to recycle we just 75675 allocate a new one. This doesn't scale well for a massive number of sounds being played 75676 simultaneously as we don't ever actually free the sound objects. Some kind of garbage 75677 collection routine might be valuable for this which I'll think about. 75678 */ 75679 ma_spinlock_lock(&pEngine->inlinedSoundLock); 75680 { 75681 ma_uint32 soundFlags = 0; 75682 75683 for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { 75684 if (ma_sound_at_end(&pNextSound->sound)) { 75685 /* 75686 The sound is at the end which means it's available for recycling. All we need to do 75687 is uninitialize it and reinitialize it. All we're doing is recycling memory. 75688 */ 75689 pSound = pNextSound; 75690 ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); 75691 break; 75692 } 75693 } 75694 75695 if (pSound != NULL) { 75696 /* 75697 We actually want to detach the sound from the list here. The reason is because we want the sound 75698 to be in a consistent state at the non-recycled case to simplify the logic below. 75699 */ 75700 if (pEngine->pInlinedSoundHead == pSound) { 75701 pEngine->pInlinedSoundHead = pSound->pNext; 75702 } 75703 75704 if (pSound->pPrev != NULL) { 75705 pSound->pPrev->pNext = pSound->pNext; 75706 } 75707 if (pSound->pNext != NULL) { 75708 pSound->pNext->pPrev = pSound->pPrev; 75709 } 75710 75711 /* Now the previous sound needs to be uninitialized. */ 75712 ma_sound_uninit(&pNextSound->sound); 75713 } else { 75714 /* No sound available for recycling. Allocate one now. */ 75715 pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); 75716 } 75717 75718 if (pSound != NULL) { /* Safety check for the allocation above. */ 75719 /* 75720 At this point we should have memory allocated for the inlined sound. We just need 75721 to initialize it like a normal sound now. 75722 */ 75723 soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ 75724 soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ 75725 soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ 75726 soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ 75727 75728 result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); 75729 if (result == MA_SUCCESS) { 75730 /* Now attach the sound to the graph. */ 75731 result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); 75732 if (result == MA_SUCCESS) { 75733 /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ 75734 pSound->pNext = pEngine->pInlinedSoundHead; 75735 pSound->pPrev = NULL; 75736 75737 pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ 75738 if (pSound->pNext != NULL) { 75739 pSound->pNext->pPrev = pSound; 75740 } 75741 } else { 75742 ma_free(pSound, &pEngine->allocationCallbacks); 75743 } 75744 } else { 75745 ma_free(pSound, &pEngine->allocationCallbacks); 75746 } 75747 } else { 75748 result = MA_OUT_OF_MEMORY; 75749 } 75750 } 75751 ma_spinlock_unlock(&pEngine->inlinedSoundLock); 75752 75753 if (result != MA_SUCCESS) { 75754 return result; 75755 } 75756 75757 /* Finally we can start playing the sound. */ 75758 result = ma_sound_start(&pSound->sound); 75759 if (result != MA_SUCCESS) { 75760 /* Failed to start the sound. We need to mark it for recycling and return an error. */ 75761 ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); 75762 return result; 75763 } 75764 75765 ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); 75766 return result; 75767 } 75768 75769 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) 75770 { 75771 return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); 75772 } 75773 #endif 75774 75775 75776 static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) 75777 { 75778 if (pSound == NULL) { 75779 return MA_INVALID_ARGS; 75780 } 75781 75782 MA_ZERO_OBJECT(pSound); 75783 pSound->seekTarget = MA_SEEK_TARGET_NONE; 75784 75785 if (pEngine == NULL) { 75786 return MA_INVALID_ARGS; 75787 } 75788 75789 return MA_SUCCESS; 75790 } 75791 75792 static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) 75793 { 75794 ma_result result; 75795 ma_engine_node_config engineNodeConfig; 75796 ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ 75797 75798 /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ 75799 MA_ASSERT(pEngine != NULL); 75800 MA_ASSERT(pSound != NULL); 75801 75802 if (pConfig == NULL) { 75803 return MA_INVALID_ARGS; 75804 } 75805 75806 pSound->pDataSource = pConfig->pDataSource; 75807 75808 if (pConfig->pDataSource != NULL) { 75809 type = ma_engine_node_type_sound; 75810 } else { 75811 type = ma_engine_node_type_group; 75812 } 75813 75814 /* 75815 Sounds are engine nodes. Before we can initialize this we need to determine the channel count. 75816 If we can't do this we need to abort. It's up to the caller to ensure they're using a data 75817 source that provides this information upfront. 75818 */ 75819 engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); 75820 engineNodeConfig.channelsIn = pConfig->channelsIn; 75821 engineNodeConfig.channelsOut = pConfig->channelsOut; 75822 engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; 75823 engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; 75824 75825 if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { 75826 engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; 75827 } 75828 75829 /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ 75830 if (pConfig->pDataSource != NULL) { 75831 result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); 75832 if (result != MA_SUCCESS) { 75833 return result; /* Failed to retrieve the channel count. */ 75834 } 75835 75836 if (engineNodeConfig.channelsIn == 0) { 75837 return MA_INVALID_OPERATION; /* Invalid channel count. */ 75838 } 75839 75840 if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { 75841 engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; 75842 } 75843 } 75844 75845 75846 /* Getting here means we should have a valid channel count and we can initialize the engine node. */ 75847 result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); 75848 if (result != MA_SUCCESS) { 75849 return result; 75850 } 75851 75852 /* If no attachment is specified, attach the sound straight to the endpoint. */ 75853 if (pConfig->pInitialAttachment == NULL) { 75854 /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ 75855 if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { 75856 result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); 75857 } 75858 } else { 75859 /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ 75860 result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); 75861 } 75862 75863 if (result != MA_SUCCESS) { 75864 ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); 75865 return result; 75866 } 75867 75868 75869 /* Apply initial range and looping state to the data source if applicable. */ 75870 if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { 75871 ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); 75872 } 75873 75874 if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { 75875 ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); 75876 } 75877 75878 ma_sound_set_looping(pSound, pConfig->isLooping); 75879 75880 return MA_SUCCESS; 75881 } 75882 75883 #ifndef MA_NO_RESOURCE_MANAGER 75884 MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) 75885 { 75886 ma_result result = MA_SUCCESS; 75887 ma_uint32 flags; 75888 ma_sound_config config; 75889 ma_resource_manager_pipeline_notifications notifications; 75890 75891 /* 75892 The engine requires knowledge of the channel count of the underlying data source before it can 75893 initialize the sound. Therefore, we need to make the resource manager wait until initialization 75894 of the underlying data source to be initialized so we can get access to the channel count. To 75895 do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. 75896 75897 Because we're initializing the data source before the sound, there's a chance the notification 75898 will get triggered before this function returns. This is OK, so long as the caller is aware of 75899 it and can avoid accessing the sound from within the notification. 75900 */ 75901 flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; 75902 75903 pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); 75904 if (pSound->pResourceManagerDataSource == NULL) { 75905 return MA_OUT_OF_MEMORY; 75906 } 75907 75908 /* Removed in 0.12. Set pDoneFence on the notifications. */ 75909 notifications = pConfig->initNotifications; 75910 if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { 75911 notifications.done.pFence = pConfig->pDoneFence; 75912 } 75913 75914 /* 75915 We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does 75916 not return prematurely before the sound has finished initializing. 75917 */ 75918 if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } 75919 { 75920 ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); 75921 resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; 75922 resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; 75923 resourceManagerDataSourceConfig.flags = flags; 75924 resourceManagerDataSourceConfig.pNotifications = ¬ifications; 75925 resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; 75926 resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; 75927 resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; 75928 resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; 75929 resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; 75930 resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; 75931 75932 result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); 75933 if (result != MA_SUCCESS) { 75934 goto done; 75935 } 75936 75937 pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ 75938 75939 /* We need to use a slightly customized version of the config so we'll need to make a copy. */ 75940 config = *pConfig; 75941 config.pFilePath = NULL; 75942 config.pFilePathW = NULL; 75943 config.pDataSource = pSound->pResourceManagerDataSource; 75944 75945 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); 75946 if (result != MA_SUCCESS) { 75947 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); 75948 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); 75949 MA_ZERO_OBJECT(pSound); 75950 goto done; 75951 } 75952 } 75953 done: 75954 if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } 75955 return result; 75956 } 75957 75958 MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) 75959 { 75960 ma_sound_config config; 75961 75962 if (pFilePath == NULL) { 75963 return MA_INVALID_ARGS; 75964 } 75965 75966 config = ma_sound_config_init_2(pEngine); 75967 config.pFilePath = pFilePath; 75968 config.flags = flags; 75969 config.pInitialAttachment = pGroup; 75970 config.pDoneFence = pDoneFence; 75971 75972 return ma_sound_init_ex(pEngine, &config, pSound); 75973 } 75974 75975 MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) 75976 { 75977 ma_sound_config config; 75978 75979 if (pFilePath == NULL) { 75980 return MA_INVALID_ARGS; 75981 } 75982 75983 config = ma_sound_config_init_2(pEngine); 75984 config.pFilePathW = pFilePath; 75985 config.flags = flags; 75986 config.pInitialAttachment = pGroup; 75987 config.pDoneFence = pDoneFence; 75988 75989 return ma_sound_init_ex(pEngine, &config, pSound); 75990 } 75991 75992 MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) 75993 { 75994 ma_result result; 75995 ma_sound_config config; 75996 75997 result = ma_sound_preinit(pEngine, pSound); 75998 if (result != MA_SUCCESS) { 75999 return result; 76000 } 76001 76002 if (pExistingSound == NULL) { 76003 return MA_INVALID_ARGS; 76004 } 76005 76006 /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ 76007 if (pExistingSound->pResourceManagerDataSource == NULL) { 76008 return MA_INVALID_OPERATION; 76009 } 76010 76011 /* 76012 We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) 76013 this will fail. 76014 */ 76015 pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); 76016 if (pSound->pResourceManagerDataSource == NULL) { 76017 return MA_OUT_OF_MEMORY; 76018 } 76019 76020 result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); 76021 if (result != MA_SUCCESS) { 76022 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); 76023 return result; 76024 } 76025 76026 config = ma_sound_config_init_2(pEngine); 76027 config.pDataSource = pSound->pResourceManagerDataSource; 76028 config.flags = flags; 76029 config.pInitialAttachment = pGroup; 76030 config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; 76031 config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; 76032 76033 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); 76034 if (result != MA_SUCCESS) { 76035 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); 76036 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); 76037 MA_ZERO_OBJECT(pSound); 76038 return result; 76039 } 76040 76041 /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ 76042 pSound->ownsDataSource = MA_TRUE; 76043 76044 return MA_SUCCESS; 76045 } 76046 #endif 76047 76048 MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) 76049 { 76050 ma_sound_config config = ma_sound_config_init_2(pEngine); 76051 config.pDataSource = pDataSource; 76052 config.flags = flags; 76053 config.pInitialAttachment = pGroup; 76054 return ma_sound_init_ex(pEngine, &config, pSound); 76055 } 76056 76057 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) 76058 { 76059 ma_result result; 76060 76061 result = ma_sound_preinit(pEngine, pSound); 76062 if (result != MA_SUCCESS) { 76063 return result; 76064 } 76065 76066 if (pConfig == NULL) { 76067 return MA_INVALID_ARGS; 76068 } 76069 76070 pSound->endCallback = pConfig->endCallback; 76071 pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; 76072 76073 /* We need to load the sound differently depending on whether or not we're loading from a file. */ 76074 #ifndef MA_NO_RESOURCE_MANAGER 76075 if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { 76076 return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); 76077 } else 76078 #endif 76079 { 76080 /* 76081 Getting here means we're not loading from a file. We may be loading from an already-initialized 76082 data source, or none at all. If we aren't specifying any data source, we'll be initializing the 76083 the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this 76084 for us, so no special treatment required here. 76085 */ 76086 return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); 76087 } 76088 } 76089 76090 MA_API void ma_sound_uninit(ma_sound* pSound) 76091 { 76092 if (pSound == NULL) { 76093 return; 76094 } 76095 76096 /* 76097 Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done 76098 so which makes thread safety beyond this point trivial. 76099 */ 76100 ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); 76101 76102 /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ 76103 #ifndef MA_NO_RESOURCE_MANAGER 76104 if (pSound->ownsDataSource) { 76105 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); 76106 ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); 76107 pSound->pDataSource = NULL; 76108 } 76109 #else 76110 MA_ASSERT(pSound->ownsDataSource == MA_FALSE); 76111 #endif 76112 } 76113 76114 MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) 76115 { 76116 if (pSound == NULL) { 76117 return NULL; 76118 } 76119 76120 return pSound->engineNode.pEngine; 76121 } 76122 76123 MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) 76124 { 76125 if (pSound == NULL) { 76126 return NULL; 76127 } 76128 76129 return pSound->pDataSource; 76130 } 76131 76132 MA_API ma_result ma_sound_start(ma_sound* pSound) 76133 { 76134 if (pSound == NULL) { 76135 return MA_INVALID_ARGS; 76136 } 76137 76138 /* If the sound is already playing, do nothing. */ 76139 if (ma_sound_is_playing(pSound)) { 76140 return MA_SUCCESS; 76141 } 76142 76143 /* If the sound is at the end it means we want to start from the start again. */ 76144 if (ma_sound_at_end(pSound)) { 76145 ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); 76146 if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { 76147 return result; /* Failed to seek back to the start. */ 76148 } 76149 76150 /* Make sure we clear the end indicator. */ 76151 ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); 76152 } 76153 76154 /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ 76155 ma_node_set_state(pSound, ma_node_state_started); 76156 76157 return MA_SUCCESS; 76158 } 76159 76160 MA_API ma_result ma_sound_stop(ma_sound* pSound) 76161 { 76162 if (pSound == NULL) { 76163 return MA_INVALID_ARGS; 76164 } 76165 76166 /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ 76167 ma_node_set_state(pSound, ma_node_state_stopped); 76168 76169 return MA_SUCCESS; 76170 } 76171 76172 MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) 76173 { 76174 if (pSound == NULL) { 76175 return MA_INVALID_ARGS; 76176 } 76177 76178 /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ 76179 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); 76180 76181 return MA_SUCCESS; 76182 } 76183 76184 MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) 76185 { 76186 ma_uint64 sampleRate; 76187 76188 if (pSound == NULL) { 76189 return MA_INVALID_ARGS; 76190 } 76191 76192 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); 76193 76194 return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); 76195 } 76196 76197 MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) 76198 { 76199 if (pSound == NULL) { 76200 return; 76201 } 76202 76203 ma_engine_node_set_volume(&pSound->engineNode, volume); 76204 } 76205 76206 MA_API float ma_sound_get_volume(const ma_sound* pSound) 76207 { 76208 float volume = 0; 76209 76210 if (pSound == NULL) { 76211 return 0; 76212 } 76213 76214 ma_engine_node_get_volume(&pSound->engineNode, &volume); 76215 76216 return volume; 76217 } 76218 76219 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) 76220 { 76221 if (pSound == NULL) { 76222 return; 76223 } 76224 76225 ma_panner_set_pan(&pSound->engineNode.panner, pan); 76226 } 76227 76228 MA_API float ma_sound_get_pan(const ma_sound* pSound) 76229 { 76230 if (pSound == NULL) { 76231 return 0; 76232 } 76233 76234 return ma_panner_get_pan(&pSound->engineNode.panner); 76235 } 76236 76237 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) 76238 { 76239 if (pSound == NULL) { 76240 return; 76241 } 76242 76243 ma_panner_set_mode(&pSound->engineNode.panner, panMode); 76244 } 76245 76246 MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) 76247 { 76248 if (pSound == NULL) { 76249 return ma_pan_mode_balance; 76250 } 76251 76252 return ma_panner_get_mode(&pSound->engineNode.panner); 76253 } 76254 76255 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) 76256 { 76257 if (pSound == NULL) { 76258 return; 76259 } 76260 76261 if (pitch <= 0) { 76262 return; 76263 } 76264 76265 ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); 76266 } 76267 76268 MA_API float ma_sound_get_pitch(const ma_sound* pSound) 76269 { 76270 if (pSound == NULL) { 76271 return 0; 76272 } 76273 76274 return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ 76275 } 76276 76277 MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) 76278 { 76279 if (pSound == NULL) { 76280 return; 76281 } 76282 76283 ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); 76284 } 76285 76286 MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) 76287 { 76288 if (pSound == NULL) { 76289 return MA_FALSE; 76290 } 76291 76292 return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); 76293 } 76294 76295 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) 76296 { 76297 if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { 76298 return; 76299 } 76300 76301 ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); 76302 } 76303 76304 MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) 76305 { 76306 if (pSound == NULL) { 76307 return MA_LISTENER_INDEX_CLOSEST; 76308 } 76309 76310 return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); 76311 } 76312 76313 MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) 76314 { 76315 ma_uint32 listenerIndex; 76316 76317 if (pSound == NULL) { 76318 return 0; 76319 } 76320 76321 listenerIndex = ma_sound_get_pinned_listener_index(pSound); 76322 if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { 76323 ma_vec3f position = ma_sound_get_position(pSound); 76324 return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); 76325 } 76326 76327 return listenerIndex; 76328 } 76329 76330 MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) 76331 { 76332 ma_vec3f relativePos; 76333 ma_engine* pEngine; 76334 76335 if (pSound == NULL) { 76336 return ma_vec3f_init_3f(0, 0, -1); 76337 } 76338 76339 pEngine = ma_sound_get_engine(pSound); 76340 if (pEngine == NULL) { 76341 return ma_vec3f_init_3f(0, 0, -1); 76342 } 76343 76344 ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); 76345 76346 return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); 76347 } 76348 76349 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) 76350 { 76351 if (pSound == NULL) { 76352 return; 76353 } 76354 76355 ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); 76356 } 76357 76358 MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) 76359 { 76360 if (pSound == NULL) { 76361 return ma_vec3f_init_3f(0, 0, 0); 76362 } 76363 76364 return ma_spatializer_get_position(&pSound->engineNode.spatializer); 76365 } 76366 76367 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) 76368 { 76369 if (pSound == NULL) { 76370 return; 76371 } 76372 76373 ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); 76374 } 76375 76376 MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) 76377 { 76378 if (pSound == NULL) { 76379 return ma_vec3f_init_3f(0, 0, 0); 76380 } 76381 76382 return ma_spatializer_get_direction(&pSound->engineNode.spatializer); 76383 } 76384 76385 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) 76386 { 76387 if (pSound == NULL) { 76388 return; 76389 } 76390 76391 ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); 76392 } 76393 76394 MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) 76395 { 76396 if (pSound == NULL) { 76397 return ma_vec3f_init_3f(0, 0, 0); 76398 } 76399 76400 return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); 76401 } 76402 76403 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) 76404 { 76405 if (pSound == NULL) { 76406 return; 76407 } 76408 76409 ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); 76410 } 76411 76412 MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) 76413 { 76414 if (pSound == NULL) { 76415 return ma_attenuation_model_none; 76416 } 76417 76418 return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); 76419 } 76420 76421 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) 76422 { 76423 if (pSound == NULL) { 76424 return; 76425 } 76426 76427 ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); 76428 } 76429 76430 MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) 76431 { 76432 if (pSound == NULL) { 76433 return ma_positioning_absolute; 76434 } 76435 76436 return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); 76437 } 76438 76439 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) 76440 { 76441 if (pSound == NULL) { 76442 return; 76443 } 76444 76445 ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); 76446 } 76447 76448 MA_API float ma_sound_get_rolloff(const ma_sound* pSound) 76449 { 76450 if (pSound == NULL) { 76451 return 0; 76452 } 76453 76454 return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); 76455 } 76456 76457 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) 76458 { 76459 if (pSound == NULL) { 76460 return; 76461 } 76462 76463 ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); 76464 } 76465 76466 MA_API float ma_sound_get_min_gain(const ma_sound* pSound) 76467 { 76468 if (pSound == NULL) { 76469 return 0; 76470 } 76471 76472 return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); 76473 } 76474 76475 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) 76476 { 76477 if (pSound == NULL) { 76478 return; 76479 } 76480 76481 ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); 76482 } 76483 76484 MA_API float ma_sound_get_max_gain(const ma_sound* pSound) 76485 { 76486 if (pSound == NULL) { 76487 return 0; 76488 } 76489 76490 return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); 76491 } 76492 76493 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) 76494 { 76495 if (pSound == NULL) { 76496 return; 76497 } 76498 76499 ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); 76500 } 76501 76502 MA_API float ma_sound_get_min_distance(const ma_sound* pSound) 76503 { 76504 if (pSound == NULL) { 76505 return 0; 76506 } 76507 76508 return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); 76509 } 76510 76511 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) 76512 { 76513 if (pSound == NULL) { 76514 return; 76515 } 76516 76517 ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); 76518 } 76519 76520 MA_API float ma_sound_get_max_distance(const ma_sound* pSound) 76521 { 76522 if (pSound == NULL) { 76523 return 0; 76524 } 76525 76526 return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); 76527 } 76528 76529 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) 76530 { 76531 if (pSound == NULL) { 76532 return; 76533 } 76534 76535 ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); 76536 } 76537 76538 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) 76539 { 76540 if (pInnerAngleInRadians != NULL) { 76541 *pInnerAngleInRadians = 0; 76542 } 76543 76544 if (pOuterAngleInRadians != NULL) { 76545 *pOuterAngleInRadians = 0; 76546 } 76547 76548 if (pOuterGain != NULL) { 76549 *pOuterGain = 0; 76550 } 76551 76552 if (pSound == NULL) { 76553 return; 76554 } 76555 76556 ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); 76557 } 76558 76559 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) 76560 { 76561 if (pSound == NULL) { 76562 return; 76563 } 76564 76565 ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); 76566 } 76567 76568 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) 76569 { 76570 if (pSound == NULL) { 76571 return 0; 76572 } 76573 76574 return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); 76575 } 76576 76577 MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) 76578 { 76579 if (pSound == NULL) { 76580 return; 76581 } 76582 76583 ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); 76584 } 76585 76586 MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) 76587 { 76588 if (pSound == NULL) { 76589 return 1; 76590 } 76591 76592 return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); 76593 } 76594 76595 76596 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) 76597 { 76598 if (pSound == NULL) { 76599 return; 76600 } 76601 76602 ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); 76603 } 76604 76605 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) 76606 { 76607 if (pSound == NULL) { 76608 return; 76609 } 76610 76611 ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); 76612 } 76613 76614 MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) 76615 { 76616 if (pSound == NULL) { 76617 return; 76618 } 76619 76620 /* 76621 We don't want to update the fader at this point because we need to use the engine's current time 76622 to derive the fader's start offset. The timer is being updated on the audio thread so in order to 76623 do this as accurately as possible we'll need to defer this to the audio thread. 76624 */ 76625 ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); 76626 ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); 76627 ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); 76628 ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); 76629 } 76630 76631 MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) 76632 { 76633 ma_uint32 sampleRate; 76634 76635 if (pSound == NULL) { 76636 return; 76637 } 76638 76639 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); 76640 76641 ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); 76642 } 76643 76644 MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) 76645 { 76646 if (pSound == NULL) { 76647 return MA_INVALID_ARGS; 76648 } 76649 76650 return ma_fader_get_current_volume(&pSound->engineNode.fader); 76651 } 76652 76653 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) 76654 { 76655 if (pSound == NULL) { 76656 return; 76657 } 76658 76659 ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); 76660 } 76661 76662 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) 76663 { 76664 if (pSound == NULL) { 76665 return; 76666 } 76667 76668 ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); 76669 } 76670 76671 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) 76672 { 76673 if (pSound == NULL) { 76674 return; 76675 } 76676 76677 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); 76678 } 76679 76680 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) 76681 { 76682 if (pSound == NULL) { 76683 return; 76684 } 76685 76686 ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); 76687 } 76688 76689 MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) 76690 { 76691 if (pSound == NULL) { 76692 return; 76693 } 76694 76695 if (fadeLengthInFrames > 0) { 76696 if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { 76697 fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; 76698 } 76699 76700 ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); 76701 } 76702 76703 ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); 76704 } 76705 76706 MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) 76707 { 76708 ma_uint32 sampleRate; 76709 76710 if (pSound == NULL) { 76711 return; 76712 } 76713 76714 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); 76715 76716 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); 76717 } 76718 76719 MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) 76720 { 76721 if (pSound == NULL) { 76722 return MA_FALSE; 76723 } 76724 76725 return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; 76726 } 76727 76728 MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) 76729 { 76730 if (pSound == NULL) { 76731 return 0; 76732 } 76733 76734 return ma_node_get_time(pSound); 76735 } 76736 76737 MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) 76738 { 76739 return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); 76740 } 76741 76742 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) 76743 { 76744 if (pSound == NULL) { 76745 return; 76746 } 76747 76748 /* Looping is only a valid concept if the sound is backed by a data source. */ 76749 if (pSound->pDataSource == NULL) { 76750 return; 76751 } 76752 76753 /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ 76754 ma_data_source_set_looping(pSound->pDataSource, isLooping); 76755 } 76756 76757 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) 76758 { 76759 if (pSound == NULL) { 76760 return MA_FALSE; 76761 } 76762 76763 /* There is no notion of looping for sounds that are not backed by a data source. */ 76764 if (pSound->pDataSource == NULL) { 76765 return MA_FALSE; 76766 } 76767 76768 return ma_data_source_is_looping(pSound->pDataSource); 76769 } 76770 76771 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) 76772 { 76773 if (pSound == NULL) { 76774 return MA_FALSE; 76775 } 76776 76777 /* There is no notion of an end of a sound if it's not backed by a data source. */ 76778 if (pSound->pDataSource == NULL) { 76779 return MA_FALSE; 76780 } 76781 76782 return ma_sound_get_at_end(pSound); 76783 } 76784 76785 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) 76786 { 76787 if (pSound == NULL) { 76788 return MA_INVALID_ARGS; 76789 } 76790 76791 /* Seeking is only valid for sounds that are backed by a data source. */ 76792 if (pSound->pDataSource == NULL) { 76793 return MA_INVALID_OPERATION; 76794 } 76795 76796 /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ 76797 ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); 76798 76799 return MA_SUCCESS; 76800 } 76801 76802 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) 76803 { 76804 if (pSound == NULL) { 76805 return MA_INVALID_ARGS; 76806 } 76807 76808 /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ 76809 if (pSound->pDataSource == NULL) { 76810 ma_uint32 channels; 76811 76812 if (pFormat != NULL) { 76813 *pFormat = ma_format_f32; 76814 } 76815 76816 channels = ma_node_get_input_channels(&pSound->engineNode, 0); 76817 if (pChannels != NULL) { 76818 *pChannels = channels; 76819 } 76820 76821 if (pSampleRate != NULL) { 76822 *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; 76823 } 76824 76825 if (pChannelMap != NULL) { 76826 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); 76827 } 76828 76829 return MA_SUCCESS; 76830 } else { 76831 return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); 76832 } 76833 } 76834 76835 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) 76836 { 76837 ma_uint64 seekTarget; 76838 76839 if (pSound == NULL) { 76840 return MA_INVALID_ARGS; 76841 } 76842 76843 /* The notion of a cursor is only valid for sounds that are backed by a data source. */ 76844 if (pSound->pDataSource == NULL) { 76845 return MA_INVALID_OPERATION; 76846 } 76847 76848 seekTarget = ma_atomic_load_64(&pSound->seekTarget); 76849 if (seekTarget != MA_SEEK_TARGET_NONE) { 76850 *pCursor = seekTarget; 76851 return MA_SUCCESS; 76852 } else { 76853 return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); 76854 } 76855 } 76856 76857 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) 76858 { 76859 if (pSound == NULL) { 76860 return MA_INVALID_ARGS; 76861 } 76862 76863 /* The notion of a sound length is only valid for sounds that are backed by a data source. */ 76864 if (pSound->pDataSource == NULL) { 76865 return MA_INVALID_OPERATION; 76866 } 76867 76868 return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); 76869 } 76870 76871 MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) 76872 { 76873 ma_result result; 76874 ma_uint64 cursorInPCMFrames; 76875 ma_uint32 sampleRate; 76876 76877 if (pCursor != NULL) { 76878 *pCursor = 0; 76879 } 76880 76881 result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); 76882 if (result != MA_SUCCESS) { 76883 return result; 76884 } 76885 76886 result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); 76887 if (result != MA_SUCCESS) { 76888 return result; 76889 } 76890 76891 /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ 76892 *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; 76893 76894 return MA_SUCCESS; 76895 } 76896 76897 MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) 76898 { 76899 if (pSound == NULL) { 76900 return MA_INVALID_ARGS; 76901 } 76902 76903 /* The notion of a sound length is only valid for sounds that are backed by a data source. */ 76904 if (pSound->pDataSource == NULL) { 76905 return MA_INVALID_OPERATION; 76906 } 76907 76908 return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); 76909 } 76910 76911 MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) 76912 { 76913 if (pSound == NULL) { 76914 return MA_INVALID_ARGS; 76915 } 76916 76917 /* The notion of an end is only valid for sounds that are backed by a data source. */ 76918 if (pSound->pDataSource == NULL) { 76919 return MA_INVALID_OPERATION; 76920 } 76921 76922 pSound->endCallback = callback; 76923 pSound->pEndCallbackUserData = pUserData; 76924 76925 return MA_SUCCESS; 76926 } 76927 76928 76929 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) 76930 { 76931 ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); 76932 config.flags = flags; 76933 config.pInitialAttachment = pParentGroup; 76934 return ma_sound_group_init_ex(pEngine, &config, pGroup); 76935 } 76936 76937 MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) 76938 { 76939 ma_sound_config soundConfig; 76940 76941 if (pGroup == NULL) { 76942 return MA_INVALID_ARGS; 76943 } 76944 76945 MA_ZERO_OBJECT(pGroup); 76946 76947 if (pConfig == NULL) { 76948 return MA_INVALID_ARGS; 76949 } 76950 76951 /* A sound group is just a sound without a data source. */ 76952 soundConfig = *pConfig; 76953 soundConfig.pFilePath = NULL; 76954 soundConfig.pFilePathW = NULL; 76955 soundConfig.pDataSource = NULL; 76956 76957 /* 76958 Groups need to have spatialization disabled by default because I think it'll be pretty rare 76959 that programs will want to spatialize groups (but not unheard of). Certainly it feels like 76960 disabling this by default feels like the right option. Spatialization can be enabled with a 76961 call to ma_sound_group_set_spatialization_enabled(). 76962 */ 76963 soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; 76964 76965 return ma_sound_init_ex(pEngine, &soundConfig, pGroup); 76966 } 76967 76968 MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) 76969 { 76970 ma_sound_uninit(pGroup); 76971 } 76972 76973 MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) 76974 { 76975 return ma_sound_get_engine(pGroup); 76976 } 76977 76978 MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) 76979 { 76980 return ma_sound_start(pGroup); 76981 } 76982 76983 MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) 76984 { 76985 return ma_sound_stop(pGroup); 76986 } 76987 76988 MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) 76989 { 76990 ma_sound_set_volume(pGroup, volume); 76991 } 76992 76993 MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) 76994 { 76995 return ma_sound_get_volume(pGroup); 76996 } 76997 76998 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) 76999 { 77000 ma_sound_set_pan(pGroup, pan); 77001 } 77002 77003 MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) 77004 { 77005 return ma_sound_get_pan(pGroup); 77006 } 77007 77008 MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) 77009 { 77010 ma_sound_set_pan_mode(pGroup, panMode); 77011 } 77012 77013 MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) 77014 { 77015 return ma_sound_get_pan_mode(pGroup); 77016 } 77017 77018 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) 77019 { 77020 ma_sound_set_pitch(pGroup, pitch); 77021 } 77022 77023 MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) 77024 { 77025 return ma_sound_get_pitch(pGroup); 77026 } 77027 77028 MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) 77029 { 77030 ma_sound_set_spatialization_enabled(pGroup, enabled); 77031 } 77032 77033 MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) 77034 { 77035 return ma_sound_is_spatialization_enabled(pGroup); 77036 } 77037 77038 MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) 77039 { 77040 ma_sound_set_pinned_listener_index(pGroup, listenerIndex); 77041 } 77042 77043 MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) 77044 { 77045 return ma_sound_get_pinned_listener_index(pGroup); 77046 } 77047 77048 MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) 77049 { 77050 return ma_sound_get_listener_index(pGroup); 77051 } 77052 77053 MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) 77054 { 77055 return ma_sound_get_direction_to_listener(pGroup); 77056 } 77057 77058 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) 77059 { 77060 ma_sound_set_position(pGroup, x, y, z); 77061 } 77062 77063 MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) 77064 { 77065 return ma_sound_get_position(pGroup); 77066 } 77067 77068 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) 77069 { 77070 ma_sound_set_direction(pGroup, x, y, z); 77071 } 77072 77073 MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) 77074 { 77075 return ma_sound_get_direction(pGroup); 77076 } 77077 77078 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) 77079 { 77080 ma_sound_set_velocity(pGroup, x, y, z); 77081 } 77082 77083 MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) 77084 { 77085 return ma_sound_get_velocity(pGroup); 77086 } 77087 77088 MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) 77089 { 77090 ma_sound_set_attenuation_model(pGroup, attenuationModel); 77091 } 77092 77093 MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) 77094 { 77095 return ma_sound_get_attenuation_model(pGroup); 77096 } 77097 77098 MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) 77099 { 77100 ma_sound_set_positioning(pGroup, positioning); 77101 } 77102 77103 MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) 77104 { 77105 return ma_sound_get_positioning(pGroup); 77106 } 77107 77108 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) 77109 { 77110 ma_sound_set_rolloff(pGroup, rolloff); 77111 } 77112 77113 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) 77114 { 77115 return ma_sound_get_rolloff(pGroup); 77116 } 77117 77118 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) 77119 { 77120 ma_sound_set_min_gain(pGroup, minGain); 77121 } 77122 77123 MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) 77124 { 77125 return ma_sound_get_min_gain(pGroup); 77126 } 77127 77128 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) 77129 { 77130 ma_sound_set_max_gain(pGroup, maxGain); 77131 } 77132 77133 MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) 77134 { 77135 return ma_sound_get_max_gain(pGroup); 77136 } 77137 77138 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) 77139 { 77140 ma_sound_set_min_distance(pGroup, minDistance); 77141 } 77142 77143 MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) 77144 { 77145 return ma_sound_get_min_distance(pGroup); 77146 } 77147 77148 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) 77149 { 77150 ma_sound_set_max_distance(pGroup, maxDistance); 77151 } 77152 77153 MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) 77154 { 77155 return ma_sound_get_max_distance(pGroup); 77156 } 77157 77158 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) 77159 { 77160 ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); 77161 } 77162 77163 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) 77164 { 77165 ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); 77166 } 77167 77168 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) 77169 { 77170 ma_sound_set_doppler_factor(pGroup, dopplerFactor); 77171 } 77172 77173 MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) 77174 { 77175 return ma_sound_get_doppler_factor(pGroup); 77176 } 77177 77178 MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) 77179 { 77180 ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); 77181 } 77182 77183 MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) 77184 { 77185 return ma_sound_get_directional_attenuation_factor(pGroup); 77186 } 77187 77188 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) 77189 { 77190 ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); 77191 } 77192 77193 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) 77194 { 77195 ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); 77196 } 77197 77198 MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) 77199 { 77200 return ma_sound_get_current_fade_volume(pGroup); 77201 } 77202 77203 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) 77204 { 77205 ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); 77206 } 77207 77208 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) 77209 { 77210 ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); 77211 } 77212 77213 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) 77214 { 77215 ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); 77216 } 77217 77218 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) 77219 { 77220 ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); 77221 } 77222 77223 MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) 77224 { 77225 return ma_sound_is_playing(pGroup); 77226 } 77227 77228 MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) 77229 { 77230 return ma_sound_get_time_in_pcm_frames(pGroup); 77231 } 77232 #endif /* MA_NO_ENGINE */ 77233 /* END SECTION: miniaudio_engine.c */ 77234 77235 77236 77237 /************************************************************************************************************************************************************** 77238 *************************************************************************************************************************************************************** 77239 77240 Auto Generated 77241 ============== 77242 All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the 77243 code below please report the bug to the respective repository for the relevant project (probably dr_libs). 77244 77245 *************************************************************************************************************************************************************** 77246 **************************************************************************************************************************************************************/ 77247 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) 77248 #if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ 77249 /* dr_wav_c begin */ 77250 #ifndef ma_dr_wav_c 77251 #define ma_dr_wav_c 77252 #ifdef __MRC__ 77253 #pragma options opt off 77254 #endif 77255 #include <stdlib.h> 77256 #include <string.h> 77257 #include <limits.h> 77258 #ifndef MA_DR_WAV_NO_STDIO 77259 #include <stdio.h> 77260 #ifndef MA_DR_WAV_NO_WCHAR 77261 #include <wchar.h> 77262 #endif 77263 #endif 77264 #ifndef MA_DR_WAV_ASSERT 77265 #include <assert.h> 77266 #define MA_DR_WAV_ASSERT(expression) assert(expression) 77267 #endif 77268 #ifndef MA_DR_WAV_MALLOC 77269 #define MA_DR_WAV_MALLOC(sz) malloc((sz)) 77270 #endif 77271 #ifndef MA_DR_WAV_REALLOC 77272 #define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) 77273 #endif 77274 #ifndef MA_DR_WAV_FREE 77275 #define MA_DR_WAV_FREE(p) free((p)) 77276 #endif 77277 #ifndef MA_DR_WAV_COPY_MEMORY 77278 #define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) 77279 #endif 77280 #ifndef MA_DR_WAV_ZERO_MEMORY 77281 #define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) 77282 #endif 77283 #ifndef MA_DR_WAV_ZERO_OBJECT 77284 #define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) 77285 #endif 77286 #define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) 77287 #define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) 77288 #define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) 77289 #define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) 77290 #define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) 77291 #define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) 77292 #define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 77293 #define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) 77294 #define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) 77295 #if defined(_MSC_VER) && _MSC_VER >= 1400 77296 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC 77297 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC 77298 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC 77299 #elif defined(__clang__) 77300 #if defined(__has_builtin) 77301 #if __has_builtin(__builtin_bswap16) 77302 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC 77303 #endif 77304 #if __has_builtin(__builtin_bswap32) 77305 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC 77306 #endif 77307 #if __has_builtin(__builtin_bswap64) 77308 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC 77309 #endif 77310 #endif 77311 #elif defined(__GNUC__) 77312 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) 77313 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC 77314 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC 77315 #endif 77316 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 77317 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC 77318 #endif 77319 #endif 77320 MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) 77321 { 77322 if (pMajor) { 77323 *pMajor = MA_DR_WAV_VERSION_MAJOR; 77324 } 77325 if (pMinor) { 77326 *pMinor = MA_DR_WAV_VERSION_MINOR; 77327 } 77328 if (pRevision) { 77329 *pRevision = MA_DR_WAV_VERSION_REVISION; 77330 } 77331 } 77332 MA_API const char* ma_dr_wav_version_string(void) 77333 { 77334 return MA_DR_WAV_VERSION_STRING; 77335 } 77336 #ifndef MA_DR_WAV_MAX_SAMPLE_RATE 77337 #define MA_DR_WAV_MAX_SAMPLE_RATE 384000 77338 #endif 77339 #ifndef MA_DR_WAV_MAX_CHANNELS 77340 #define MA_DR_WAV_MAX_CHANNELS 256 77341 #endif 77342 #ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE 77343 #define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 77344 #endif 77345 static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; 77346 static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; 77347 static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; 77348 static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; 77349 static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; 77350 static MA_INLINE int ma_dr_wav__is_little_endian(void) 77351 { 77352 #if defined(MA_X86) || defined(MA_X64) 77353 return MA_TRUE; 77354 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN 77355 return MA_TRUE; 77356 #else 77357 int n = 1; 77358 return (*(char*)&n) == 1; 77359 #endif 77360 } 77361 static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) 77362 { 77363 int i; 77364 for (i = 0; i < 16; ++i) { 77365 guid[i] = data[i]; 77366 } 77367 } 77368 static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) 77369 { 77370 #ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC 77371 #if defined(_MSC_VER) 77372 return _byteswap_ushort(n); 77373 #elif defined(__GNUC__) || defined(__clang__) 77374 return __builtin_bswap16(n); 77375 #else 77376 #error "This compiler does not support the byte swap intrinsic." 77377 #endif 77378 #else 77379 return ((n & 0xFF00) >> 8) | 77380 ((n & 0x00FF) << 8); 77381 #endif 77382 } 77383 static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) 77384 { 77385 #ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC 77386 #if defined(_MSC_VER) 77387 return _byteswap_ulong(n); 77388 #elif defined(__GNUC__) || defined(__clang__) 77389 #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) 77390 ma_uint32 r; 77391 __asm__ __volatile__ ( 77392 #if defined(MA_64BIT) 77393 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) 77394 #else 77395 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) 77396 #endif 77397 ); 77398 return r; 77399 #else 77400 return __builtin_bswap32(n); 77401 #endif 77402 #else 77403 #error "This compiler does not support the byte swap intrinsic." 77404 #endif 77405 #else 77406 return ((n & 0xFF000000) >> 24) | 77407 ((n & 0x00FF0000) >> 8) | 77408 ((n & 0x0000FF00) << 8) | 77409 ((n & 0x000000FF) << 24); 77410 #endif 77411 } 77412 static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) 77413 { 77414 #ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC 77415 #if defined(_MSC_VER) 77416 return _byteswap_uint64(n); 77417 #elif defined(__GNUC__) || defined(__clang__) 77418 return __builtin_bswap64(n); 77419 #else 77420 #error "This compiler does not support the byte swap intrinsic." 77421 #endif 77422 #else 77423 return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | 77424 ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | 77425 ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | 77426 ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | 77427 ((n & ((ma_uint64)0xFF000000 )) << 8) | 77428 ((n & ((ma_uint64)0x00FF0000 )) << 24) | 77429 ((n & ((ma_uint64)0x0000FF00 )) << 40) | 77430 ((n & ((ma_uint64)0x000000FF )) << 56); 77431 #endif 77432 } 77433 static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) 77434 { 77435 return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); 77436 } 77437 static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) 77438 { 77439 ma_uint64 iSample; 77440 for (iSample = 0; iSample < sampleCount; iSample += 1) { 77441 pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); 77442 } 77443 } 77444 static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) 77445 { 77446 ma_uint8 t; 77447 t = p[0]; 77448 p[0] = p[2]; 77449 p[2] = t; 77450 } 77451 static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) 77452 { 77453 ma_uint64 iSample; 77454 for (iSample = 0; iSample < sampleCount; iSample += 1) { 77455 ma_uint8* pSample = pSamples + (iSample*3); 77456 ma_dr_wav__bswap_s24(pSample); 77457 } 77458 } 77459 static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) 77460 { 77461 return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); 77462 } 77463 static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) 77464 { 77465 ma_uint64 iSample; 77466 for (iSample = 0; iSample < sampleCount; iSample += 1) { 77467 pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); 77468 } 77469 } 77470 static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) 77471 { 77472 return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); 77473 } 77474 static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) 77475 { 77476 ma_uint64 iSample; 77477 for (iSample = 0; iSample < sampleCount; iSample += 1) { 77478 pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); 77479 } 77480 } 77481 static MA_INLINE float ma_dr_wav__bswap_f32(float n) 77482 { 77483 union { 77484 ma_uint32 i; 77485 float f; 77486 } x; 77487 x.f = n; 77488 x.i = ma_dr_wav__bswap32(x.i); 77489 return x.f; 77490 } 77491 static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) 77492 { 77493 ma_uint64 iSample; 77494 for (iSample = 0; iSample < sampleCount; iSample += 1) { 77495 pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); 77496 } 77497 } 77498 static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) 77499 { 77500 switch (bytesPerSample) 77501 { 77502 case 1: 77503 { 77504 } break; 77505 case 2: 77506 { 77507 ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); 77508 } break; 77509 case 3: 77510 { 77511 ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); 77512 } break; 77513 case 4: 77514 { 77515 ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); 77516 } break; 77517 case 8: 77518 { 77519 ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); 77520 } break; 77521 default: 77522 { 77523 MA_DR_WAV_ASSERT(MA_FALSE); 77524 } break; 77525 } 77526 } 77527 MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) 77528 { 77529 if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { 77530 return MA_TRUE; 77531 } else { 77532 return MA_FALSE; 77533 } 77534 } 77535 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) 77536 { 77537 return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); 77538 } 77539 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) 77540 { 77541 return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); 77542 } 77543 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) 77544 { 77545 if (ma_dr_wav_is_container_be(container)) { 77546 return ma_dr_wav_bytes_to_u16_be(data); 77547 } else { 77548 return ma_dr_wav_bytes_to_u16_le(data); 77549 } 77550 } 77551 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) 77552 { 77553 return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); 77554 } 77555 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) 77556 { 77557 return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); 77558 } 77559 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) 77560 { 77561 if (ma_dr_wav_is_container_be(container)) { 77562 return ma_dr_wav_bytes_to_u32_be(data); 77563 } else { 77564 return ma_dr_wav_bytes_to_u32_le(data); 77565 } 77566 } 77567 MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) 77568 { 77569 ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; 77570 ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); 77571 ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); 77572 ma_uint64 significand = (hi << 32) | lo; 77573 int sign = exponent >> 15; 77574 exponent &= 0x7FFF; 77575 if (exponent == 0 && significand == 0) { 77576 return 0; 77577 } else if (exponent == 0x7FFF) { 77578 return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; 77579 } 77580 exponent -= 16383; 77581 if (exponent > 63) { 77582 return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; 77583 } else if (exponent < 1) { 77584 return 0; 77585 } 77586 significand >>= (63 - exponent); 77587 if (sign) { 77588 return -(ma_int64)significand; 77589 } else { 77590 return (ma_int64)significand; 77591 } 77592 } 77593 MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) 77594 { 77595 (void)pUserData; 77596 return MA_DR_WAV_MALLOC(sz); 77597 } 77598 MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) 77599 { 77600 (void)pUserData; 77601 return MA_DR_WAV_REALLOC(p, sz); 77602 } 77603 MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) 77604 { 77605 (void)pUserData; 77606 MA_DR_WAV_FREE(p); 77607 } 77608 MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 77609 { 77610 if (pAllocationCallbacks == NULL) { 77611 return NULL; 77612 } 77613 if (pAllocationCallbacks->onMalloc != NULL) { 77614 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); 77615 } 77616 if (pAllocationCallbacks->onRealloc != NULL) { 77617 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); 77618 } 77619 return NULL; 77620 } 77621 MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) 77622 { 77623 if (pAllocationCallbacks == NULL) { 77624 return NULL; 77625 } 77626 if (pAllocationCallbacks->onRealloc != NULL) { 77627 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); 77628 } 77629 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { 77630 void* p2; 77631 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); 77632 if (p2 == NULL) { 77633 return NULL; 77634 } 77635 if (p != NULL) { 77636 MA_DR_WAV_COPY_MEMORY(p2, p, szOld); 77637 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 77638 } 77639 return p2; 77640 } 77641 return NULL; 77642 } 77643 MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 77644 { 77645 if (p == NULL || pAllocationCallbacks == NULL) { 77646 return; 77647 } 77648 if (pAllocationCallbacks->onFree != NULL) { 77649 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 77650 } 77651 } 77652 MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) 77653 { 77654 if (pAllocationCallbacks != NULL) { 77655 return *pAllocationCallbacks; 77656 } else { 77657 ma_allocation_callbacks allocationCallbacks; 77658 allocationCallbacks.pUserData = NULL; 77659 allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; 77660 allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; 77661 allocationCallbacks.onFree = ma_dr_wav__free_default; 77662 return allocationCallbacks; 77663 } 77664 } 77665 static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) 77666 { 77667 return 77668 formatTag == MA_DR_WAVE_FORMAT_ADPCM || 77669 formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; 77670 } 77671 MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) 77672 { 77673 return (unsigned int)(chunkSize % 2); 77674 } 77675 MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) 77676 { 77677 return (unsigned int)(chunkSize % 8); 77678 } 77679 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); 77680 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); 77681 MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); 77682 MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) 77683 { 77684 if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { 77685 ma_uint8 sizeInBytes[4]; 77686 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { 77687 return MA_AT_END; 77688 } 77689 if (onRead(pUserData, sizeInBytes, 4) != 4) { 77690 return MA_INVALID_FILE; 77691 } 77692 pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); 77693 pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); 77694 *pRunningBytesReadOut += 8; 77695 } else if (container == ma_dr_wav_container_w64) { 77696 ma_uint8 sizeInBytes[8]; 77697 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { 77698 return MA_AT_END; 77699 } 77700 if (onRead(pUserData, sizeInBytes, 8) != 8) { 77701 return MA_INVALID_FILE; 77702 } 77703 pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; 77704 pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); 77705 *pRunningBytesReadOut += 24; 77706 } else { 77707 return MA_INVALID_FILE; 77708 } 77709 return MA_SUCCESS; 77710 } 77711 MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) 77712 { 77713 ma_uint64 bytesRemainingToSeek = offset; 77714 while (bytesRemainingToSeek > 0) { 77715 if (bytesRemainingToSeek > 0x7FFFFFFF) { 77716 if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { 77717 return MA_FALSE; 77718 } 77719 bytesRemainingToSeek -= 0x7FFFFFFF; 77720 } else { 77721 if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { 77722 return MA_FALSE; 77723 } 77724 bytesRemainingToSeek = 0; 77725 } 77726 } 77727 return MA_TRUE; 77728 } 77729 MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) 77730 { 77731 if (offset <= 0x7FFFFFFF) { 77732 return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); 77733 } 77734 if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { 77735 return MA_FALSE; 77736 } 77737 offset -= 0x7FFFFFFF; 77738 for (;;) { 77739 if (offset <= 0x7FFFFFFF) { 77740 return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); 77741 } 77742 if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { 77743 return MA_FALSE; 77744 } 77745 offset -= 0x7FFFFFFF; 77746 } 77747 } 77748 MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) 77749 { 77750 size_t bytesRead; 77751 MA_DR_WAV_ASSERT(onRead != NULL); 77752 MA_DR_WAV_ASSERT(pCursor != NULL); 77753 bytesRead = onRead(pUserData, pBufferOut, bytesToRead); 77754 *pCursor += bytesRead; 77755 return bytesRead; 77756 } 77757 #if 0 77758 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) 77759 { 77760 MA_DR_WAV_ASSERT(onSeek != NULL); 77761 MA_DR_WAV_ASSERT(pCursor != NULL); 77762 if (!onSeek(pUserData, offset, origin)) { 77763 return MA_FALSE; 77764 } 77765 if (origin == ma_dr_wav_seek_origin_start) { 77766 *pCursor = offset; 77767 } else { 77768 *pCursor += offset; 77769 } 77770 return MA_TRUE; 77771 } 77772 #endif 77773 #define MA_DR_WAV_SMPL_BYTES 36 77774 #define MA_DR_WAV_SMPL_LOOP_BYTES 24 77775 #define MA_DR_WAV_INST_BYTES 7 77776 #define MA_DR_WAV_ACID_BYTES 24 77777 #define MA_DR_WAV_CUE_BYTES 4 77778 #define MA_DR_WAV_BEXT_BYTES 602 77779 #define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 77780 #define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 77781 #define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 77782 #define MA_DR_WAV_BEXT_RESERVED_BYTES 180 77783 #define MA_DR_WAV_BEXT_UMID_BYTES 64 77784 #define MA_DR_WAV_CUE_POINT_BYTES 24 77785 #define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 77786 #define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 77787 #define MA_DR_WAV_METADATA_ALIGNMENT 8 77788 typedef enum 77789 { 77790 ma_dr_wav__metadata_parser_stage_count, 77791 ma_dr_wav__metadata_parser_stage_read 77792 } ma_dr_wav__metadata_parser_stage; 77793 typedef struct 77794 { 77795 ma_dr_wav_read_proc onRead; 77796 ma_dr_wav_seek_proc onSeek; 77797 void *pReadSeekUserData; 77798 ma_dr_wav__metadata_parser_stage stage; 77799 ma_dr_wav_metadata *pMetadata; 77800 ma_uint32 metadataCount; 77801 ma_uint8 *pData; 77802 ma_uint8 *pDataCursor; 77803 ma_uint64 metadataCursor; 77804 ma_uint64 extraCapacity; 77805 } ma_dr_wav__metadata_parser; 77806 MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) 77807 { 77808 ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; 77809 if (cap > MA_SIZE_MAX) { 77810 return 0; 77811 } 77812 return (size_t)cap; 77813 } 77814 MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) 77815 { 77816 ma_uint8* pResult; 77817 if (align) { 77818 ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; 77819 if (modulo != 0) { 77820 pParser->pDataCursor += align - modulo; 77821 } 77822 } 77823 pResult = pParser->pDataCursor; 77824 MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); 77825 pParser->pDataCursor += size; 77826 return pResult; 77827 } 77828 MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) 77829 { 77830 size_t extra = bytes + (align ? (align - 1) : 0); 77831 pParser->extraCapacity += extra; 77832 } 77833 MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) 77834 { 77835 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { 77836 pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); 77837 pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); 77838 pParser->pDataCursor = pParser->pData; 77839 if (pParser->pData == NULL) { 77840 return MA_OUT_OF_MEMORY; 77841 } 77842 pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); 77843 pParser->metadataCursor = 0; 77844 } 77845 return MA_SUCCESS; 77846 } 77847 MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) 77848 { 77849 if (pCursor != NULL) { 77850 return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); 77851 } else { 77852 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); 77853 } 77854 } 77855 MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) 77856 { 77857 ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; 77858 ma_uint64 totalBytesRead = 0; 77859 size_t bytesJustRead; 77860 if (pMetadata == NULL) { 77861 return 0; 77862 } 77863 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); 77864 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 77865 MA_DR_WAV_ASSERT(pChunkHeader != NULL); 77866 if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { 77867 ma_uint32 iSampleLoop; 77868 pMetadata->type = ma_dr_wav_metadata_type_smpl; 77869 pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); 77870 pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); 77871 pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); 77872 pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); 77873 pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); 77874 pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); 77875 pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); 77876 pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); 77877 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); 77878 if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { 77879 pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); 77880 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { 77881 ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; 77882 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); 77883 if (bytesJustRead == sizeof(smplLoopData)) { 77884 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); 77885 pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); 77886 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); 77887 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); 77888 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); 77889 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); 77890 } else { 77891 break; 77892 } 77893 } 77894 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { 77895 pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); 77896 MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); 77897 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); 77898 } 77899 } 77900 } 77901 return totalBytesRead; 77902 } 77903 MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) 77904 { 77905 ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; 77906 ma_uint64 totalBytesRead = 0; 77907 size_t bytesJustRead; 77908 if (pMetadata == NULL) { 77909 return 0; 77910 } 77911 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); 77912 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 77913 if (bytesJustRead == sizeof(cueHeaderSectionData)) { 77914 pMetadata->type = ma_dr_wav_metadata_type_cue; 77915 pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); 77916 if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { 77917 pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); 77918 MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); 77919 if (pMetadata->data.cue.cuePointCount > 0) { 77920 ma_uint32 iCuePoint; 77921 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { 77922 ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; 77923 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); 77924 if (bytesJustRead == sizeof(cuePointData)) { 77925 pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); 77926 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); 77927 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; 77928 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; 77929 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; 77930 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; 77931 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); 77932 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); 77933 pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); 77934 } else { 77935 break; 77936 } 77937 } 77938 } 77939 } 77940 } 77941 return totalBytesRead; 77942 } 77943 MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) 77944 { 77945 ma_uint8 instData[MA_DR_WAV_INST_BYTES]; 77946 ma_uint64 bytesRead; 77947 if (pMetadata == NULL) { 77948 return 0; 77949 } 77950 bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); 77951 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 77952 if (bytesRead == sizeof(instData)) { 77953 pMetadata->type = ma_dr_wav_metadata_type_inst; 77954 pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; 77955 pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; 77956 pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; 77957 pMetadata->data.inst.lowNote = (ma_int8)instData[3]; 77958 pMetadata->data.inst.highNote = (ma_int8)instData[4]; 77959 pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; 77960 pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; 77961 } 77962 return bytesRead; 77963 } 77964 MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) 77965 { 77966 ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; 77967 ma_uint64 bytesRead; 77968 if (pMetadata == NULL) { 77969 return 0; 77970 } 77971 bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); 77972 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 77973 if (bytesRead == sizeof(acidData)) { 77974 pMetadata->type = ma_dr_wav_metadata_type_acid; 77975 pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); 77976 pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); 77977 pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); 77978 pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); 77979 pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); 77980 pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); 77981 pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); 77982 pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); 77983 } 77984 return bytesRead; 77985 } 77986 MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) 77987 { 77988 size_t result = 0; 77989 while (*str++) { 77990 result += 1; 77991 } 77992 return result; 77993 } 77994 MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) 77995 { 77996 size_t result = 0; 77997 while (*str++ && result < maxToRead) { 77998 result += 1; 77999 } 78000 return result; 78001 } 78002 MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) 78003 { 78004 size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); 78005 if (len) { 78006 char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); 78007 MA_DR_WAV_ASSERT(result != NULL); 78008 MA_DR_WAV_COPY_MEMORY(result, str, len); 78009 result[len] = '\0'; 78010 return result; 78011 } else { 78012 return NULL; 78013 } 78014 } 78015 typedef struct 78016 { 78017 const void* pBuffer; 78018 size_t sizeInBytes; 78019 size_t cursor; 78020 } ma_dr_wav_buffer_reader; 78021 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) 78022 { 78023 MA_DR_WAV_ASSERT(pBuffer != NULL); 78024 MA_DR_WAV_ASSERT(pReader != NULL); 78025 MA_DR_WAV_ZERO_OBJECT(pReader); 78026 pReader->pBuffer = pBuffer; 78027 pReader->sizeInBytes = sizeInBytes; 78028 pReader->cursor = 0; 78029 return MA_SUCCESS; 78030 } 78031 MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) 78032 { 78033 MA_DR_WAV_ASSERT(pReader != NULL); 78034 return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); 78035 } 78036 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) 78037 { 78038 MA_DR_WAV_ASSERT(pReader != NULL); 78039 if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { 78040 return MA_BAD_SEEK; 78041 } 78042 pReader->cursor += bytesToSeek; 78043 return MA_SUCCESS; 78044 } 78045 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) 78046 { 78047 ma_result result = MA_SUCCESS; 78048 size_t bytesRemaining; 78049 MA_DR_WAV_ASSERT(pReader != NULL); 78050 if (pBytesRead != NULL) { 78051 *pBytesRead = 0; 78052 } 78053 bytesRemaining = (pReader->sizeInBytes - pReader->cursor); 78054 if (bytesToRead > bytesRemaining) { 78055 bytesToRead = bytesRemaining; 78056 } 78057 if (pDst == NULL) { 78058 result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); 78059 } else { 78060 MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); 78061 pReader->cursor += bytesToRead; 78062 } 78063 MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); 78064 if (result == MA_SUCCESS) { 78065 if (pBytesRead != NULL) { 78066 *pBytesRead = bytesToRead; 78067 } 78068 } 78069 return MA_SUCCESS; 78070 } 78071 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) 78072 { 78073 ma_result result; 78074 size_t bytesRead; 78075 ma_uint8 data[2]; 78076 MA_DR_WAV_ASSERT(pReader != NULL); 78077 MA_DR_WAV_ASSERT(pDst != NULL); 78078 *pDst = 0; 78079 result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); 78080 if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { 78081 return result; 78082 } 78083 *pDst = ma_dr_wav_bytes_to_u16(data); 78084 return MA_SUCCESS; 78085 } 78086 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) 78087 { 78088 ma_result result; 78089 size_t bytesRead; 78090 ma_uint8 data[4]; 78091 MA_DR_WAV_ASSERT(pReader != NULL); 78092 MA_DR_WAV_ASSERT(pDst != NULL); 78093 *pDst = 0; 78094 result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); 78095 if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { 78096 return result; 78097 } 78098 *pDst = ma_dr_wav_bytes_to_u32(data); 78099 return MA_SUCCESS; 78100 } 78101 MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) 78102 { 78103 ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; 78104 size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); 78105 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 78106 if (bytesRead == sizeof(bextData)) { 78107 ma_dr_wav_buffer_reader reader; 78108 ma_uint32 timeReferenceLow; 78109 ma_uint32 timeReferenceHigh; 78110 size_t extraBytes; 78111 pMetadata->type = ma_dr_wav_metadata_type_bext; 78112 if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { 78113 pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); 78114 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); 78115 pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); 78116 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); 78117 pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); 78118 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); 78119 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); 78120 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); 78121 ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); 78122 ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); 78123 pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; 78124 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); 78125 pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); 78126 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); 78127 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); 78128 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); 78129 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); 78130 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); 78131 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); 78132 MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); 78133 extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); 78134 if (extraBytes > 0) { 78135 pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); 78136 MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); 78137 bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); 78138 pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); 78139 } else { 78140 pMetadata->data.bext.pCodingHistory = NULL; 78141 pMetadata->data.bext.codingHistorySize = 0; 78142 } 78143 } 78144 } 78145 return bytesRead; 78146 } 78147 MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) 78148 { 78149 ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; 78150 ma_uint64 totalBytesRead = 0; 78151 size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); 78152 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 78153 if (bytesJustRead == sizeof(cueIDBuffer)) { 78154 ma_uint32 sizeIncludingNullTerminator; 78155 pMetadata->type = type; 78156 pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); 78157 sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; 78158 if (sizeIncludingNullTerminator > 0) { 78159 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; 78160 pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); 78161 MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); 78162 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); 78163 } else { 78164 pMetadata->data.labelOrNote.stringLength = 0; 78165 pMetadata->data.labelOrNote.pString = NULL; 78166 } 78167 } 78168 return totalBytesRead; 78169 } 78170 MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) 78171 { 78172 ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; 78173 ma_uint64 totalBytesRead = 0; 78174 size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); 78175 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); 78176 if (bytesJustRead == sizeof(buffer)) { 78177 ma_uint32 sizeIncludingNullTerminator; 78178 pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; 78179 pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); 78180 pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); 78181 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; 78182 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; 78183 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; 78184 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; 78185 pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); 78186 pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); 78187 pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); 78188 pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); 78189 sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; 78190 if (sizeIncludingNullTerminator > 0) { 78191 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; 78192 pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); 78193 MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); 78194 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); 78195 } else { 78196 pMetadata->data.labelledCueRegion.stringLength = 0; 78197 pMetadata->data.labelledCueRegion.pString = NULL; 78198 } 78199 } 78200 return totalBytesRead; 78201 } 78202 MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) 78203 { 78204 ma_uint64 bytesRead = 0; 78205 ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; 78206 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78207 pParser->metadataCount += 1; 78208 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); 78209 } else { 78210 ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; 78211 pMetadata->type = type; 78212 if (stringSizeWithNullTerminator > 0) { 78213 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; 78214 pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); 78215 MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); 78216 bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); 78217 if (bytesRead == chunkSize) { 78218 pParser->metadataCursor += 1; 78219 } else { 78220 } 78221 } else { 78222 pMetadata->data.infoText.stringLength = 0; 78223 pMetadata->data.infoText.pString = NULL; 78224 pParser->metadataCursor += 1; 78225 } 78226 } 78227 return bytesRead; 78228 } 78229 MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) 78230 { 78231 ma_uint64 bytesRead = 0; 78232 if (location == ma_dr_wav_metadata_location_invalid) { 78233 return 0; 78234 } 78235 if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { 78236 return 0; 78237 } 78238 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78239 pParser->metadataCount += 1; 78240 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); 78241 } else { 78242 ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; 78243 pMetadata->type = ma_dr_wav_metadata_type_unknown; 78244 pMetadata->data.unknown.chunkLocation = location; 78245 pMetadata->data.unknown.id[0] = pChunkId[0]; 78246 pMetadata->data.unknown.id[1] = pChunkId[1]; 78247 pMetadata->data.unknown.id[2] = pChunkId[2]; 78248 pMetadata->data.unknown.id[3] = pChunkId[3]; 78249 pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; 78250 pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); 78251 MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); 78252 bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); 78253 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { 78254 pParser->metadataCursor += 1; 78255 } else { 78256 } 78257 } 78258 return bytesRead; 78259 } 78260 MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) 78261 { 78262 return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); 78263 } 78264 MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) 78265 { 78266 const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; 78267 ma_uint64 bytesRead = 0; 78268 if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { 78269 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { 78270 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78271 ma_uint8 buffer[4]; 78272 size_t bytesJustRead; 78273 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { 78274 return bytesRead; 78275 } 78276 bytesRead += 28; 78277 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); 78278 if (bytesJustRead == sizeof(buffer)) { 78279 ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); 78280 ma_uint64 calculatedLoopCount; 78281 calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; 78282 if (calculatedLoopCount == loopCount) { 78283 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); 78284 if (bytesJustRead == sizeof(buffer)) { 78285 ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); 78286 pParser->metadataCount += 1; 78287 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); 78288 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); 78289 } 78290 } else { 78291 } 78292 } 78293 } else { 78294 bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); 78295 if (bytesRead == pChunkHeader->sizeInBytes) { 78296 pParser->metadataCursor += 1; 78297 } else { 78298 } 78299 } 78300 } else { 78301 } 78302 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { 78303 if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { 78304 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78305 pParser->metadataCount += 1; 78306 } else { 78307 bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); 78308 if (bytesRead == pChunkHeader->sizeInBytes) { 78309 pParser->metadataCursor += 1; 78310 } else { 78311 } 78312 } 78313 } else { 78314 } 78315 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { 78316 if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { 78317 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78318 pParser->metadataCount += 1; 78319 } else { 78320 bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); 78321 if (bytesRead == pChunkHeader->sizeInBytes) { 78322 pParser->metadataCursor += 1; 78323 } else { 78324 } 78325 } 78326 } else { 78327 } 78328 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { 78329 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { 78330 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78331 size_t cueCount; 78332 pParser->metadataCount += 1; 78333 cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; 78334 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); 78335 } else { 78336 bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); 78337 if (bytesRead == pChunkHeader->sizeInBytes) { 78338 pParser->metadataCursor += 1; 78339 } else { 78340 } 78341 } 78342 } else { 78343 } 78344 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { 78345 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { 78346 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78347 char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; 78348 size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; 78349 size_t bytesJustRead; 78350 buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; 78351 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); 78352 if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { 78353 return bytesRead; 78354 } 78355 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; 78356 buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; 78357 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); 78358 if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { 78359 return bytesRead; 78360 } 78361 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; 78362 buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; 78363 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); 78364 if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { 78365 return bytesRead; 78366 } 78367 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; 78368 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; 78369 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); 78370 pParser->metadataCount += 1; 78371 } else { 78372 bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); 78373 if (bytesRead == pChunkHeader->sizeInBytes) { 78374 pParser->metadataCursor += 1; 78375 } else { 78376 } 78377 } 78378 } else { 78379 } 78380 } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { 78381 ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; 78382 while (bytesRead < pChunkHeader->sizeInBytes) { 78383 ma_uint8 subchunkId[4]; 78384 ma_uint8 subchunkSizeBuffer[4]; 78385 ma_uint64 subchunkDataSize; 78386 ma_uint64 subchunkBytesRead = 0; 78387 ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); 78388 if (bytesJustRead != sizeof(subchunkId)) { 78389 break; 78390 } 78391 if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { 78392 listType = ma_dr_wav_metadata_location_inside_adtl_list; 78393 continue; 78394 } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { 78395 listType = ma_dr_wav_metadata_location_inside_info_list; 78396 continue; 78397 } 78398 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); 78399 if (bytesJustRead != sizeof(subchunkSizeBuffer)) { 78400 break; 78401 } 78402 subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); 78403 if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { 78404 if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { 78405 ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; 78406 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78407 pParser->metadataCount += 1; 78408 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); 78409 } else { 78410 subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); 78411 if (subchunkBytesRead == subchunkDataSize) { 78412 pParser->metadataCursor += 1; 78413 } else { 78414 } 78415 } 78416 } else { 78417 } 78418 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { 78419 if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { 78420 ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; 78421 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { 78422 pParser->metadataCount += 1; 78423 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); 78424 } else { 78425 subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); 78426 if (subchunkBytesRead == subchunkDataSize) { 78427 pParser->metadataCursor += 1; 78428 } else { 78429 } 78430 } 78431 } else { 78432 } 78433 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { 78434 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); 78435 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { 78436 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); 78437 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { 78438 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); 78439 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { 78440 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); 78441 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { 78442 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); 78443 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { 78444 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); 78445 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { 78446 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); 78447 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { 78448 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); 78449 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { 78450 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); 78451 } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { 78452 subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); 78453 } 78454 bytesRead += subchunkBytesRead; 78455 MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); 78456 if (subchunkBytesRead < subchunkDataSize) { 78457 ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; 78458 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { 78459 break; 78460 } 78461 bytesRead += bytesToSeek; 78462 } 78463 if ((subchunkDataSize % 2) == 1) { 78464 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { 78465 break; 78466 } 78467 bytesRead += 1; 78468 } 78469 } 78470 } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { 78471 bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); 78472 } 78473 return bytesRead; 78474 } 78475 MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) 78476 { 78477 ma_uint32 bytesPerFrame; 78478 if ((pWav->bitsPerSample & 0x7) == 0) { 78479 bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; 78480 } else { 78481 bytesPerFrame = pWav->fmt.blockAlign; 78482 } 78483 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { 78484 if (bytesPerFrame != pWav->fmt.channels) { 78485 return 0; 78486 } 78487 } 78488 return bytesPerFrame; 78489 } 78490 MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) 78491 { 78492 if (pFMT == NULL) { 78493 return 0; 78494 } 78495 if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { 78496 return pFMT->formatTag; 78497 } else { 78498 return ma_dr_wav_bytes_to_u16(pFMT->subFormat); 78499 } 78500 } 78501 MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) 78502 { 78503 if (pWav == NULL || onRead == NULL || onSeek == NULL) { 78504 return MA_FALSE; 78505 } 78506 MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); 78507 pWav->onRead = onRead; 78508 pWav->onSeek = onSeek; 78509 pWav->pUserData = pReadSeekUserData; 78510 pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); 78511 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { 78512 return MA_FALSE; 78513 } 78514 return MA_TRUE; 78515 } 78516 MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) 78517 { 78518 ma_result result; 78519 ma_uint64 cursor; 78520 ma_bool32 sequential; 78521 ma_uint8 riff[4]; 78522 ma_dr_wav_fmt fmt; 78523 unsigned short translatedFormatTag; 78524 ma_uint64 dataChunkSize = 0; 78525 ma_uint64 sampleCountFromFactChunk = 0; 78526 ma_uint64 metadataStartPos; 78527 ma_dr_wav__metadata_parser metadataParser; 78528 ma_bool8 isProcessingMetadata = MA_FALSE; 78529 ma_bool8 foundChunk_fmt = MA_FALSE; 78530 ma_bool8 foundChunk_data = MA_FALSE; 78531 ma_bool8 isAIFCFormType = MA_FALSE; 78532 ma_uint64 aiffFrameCount = 0; 78533 cursor = 0; 78534 sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; 78535 MA_DR_WAV_ZERO_OBJECT(&fmt); 78536 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { 78537 return MA_FALSE; 78538 } 78539 if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { 78540 pWav->container = ma_dr_wav_container_riff; 78541 } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { 78542 pWav->container = ma_dr_wav_container_rifx; 78543 } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { 78544 int i; 78545 ma_uint8 riff2[12]; 78546 pWav->container = ma_dr_wav_container_w64; 78547 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { 78548 return MA_FALSE; 78549 } 78550 for (i = 0; i < 12; ++i) { 78551 if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { 78552 return MA_FALSE; 78553 } 78554 } 78555 } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { 78556 pWav->container = ma_dr_wav_container_rf64; 78557 } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { 78558 pWav->container = ma_dr_wav_container_aiff; 78559 } else { 78560 return MA_FALSE; 78561 } 78562 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { 78563 ma_uint8 chunkSizeBytes[4]; 78564 ma_uint8 wave[4]; 78565 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { 78566 return MA_FALSE; 78567 } 78568 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { 78569 if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { 78570 return MA_FALSE; 78571 } 78572 } else if (pWav->container == ma_dr_wav_container_rf64) { 78573 if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { 78574 return MA_FALSE; 78575 } 78576 } else { 78577 return MA_FALSE; 78578 } 78579 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { 78580 return MA_FALSE; 78581 } 78582 if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { 78583 return MA_FALSE; 78584 } 78585 } else if (pWav->container == ma_dr_wav_container_w64) { 78586 ma_uint8 chunkSizeBytes[8]; 78587 ma_uint8 wave[16]; 78588 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { 78589 return MA_FALSE; 78590 } 78591 if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { 78592 return MA_FALSE; 78593 } 78594 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { 78595 return MA_FALSE; 78596 } 78597 if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { 78598 return MA_FALSE; 78599 } 78600 } else if (pWav->container == ma_dr_wav_container_aiff) { 78601 ma_uint8 chunkSizeBytes[4]; 78602 ma_uint8 aiff[4]; 78603 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { 78604 return MA_FALSE; 78605 } 78606 if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { 78607 return MA_FALSE; 78608 } 78609 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { 78610 return MA_FALSE; 78611 } 78612 if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { 78613 isAIFCFormType = MA_FALSE; 78614 } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { 78615 isAIFCFormType = MA_TRUE; 78616 } else { 78617 return MA_FALSE; 78618 } 78619 } else { 78620 return MA_FALSE; 78621 } 78622 if (pWav->container == ma_dr_wav_container_rf64) { 78623 ma_uint8 sizeBytes[8]; 78624 ma_uint64 bytesRemainingInChunk; 78625 ma_dr_wav_chunk_header header; 78626 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); 78627 if (result != MA_SUCCESS) { 78628 return MA_FALSE; 78629 } 78630 if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { 78631 return MA_FALSE; 78632 } 78633 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; 78634 if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { 78635 return MA_FALSE; 78636 } 78637 bytesRemainingInChunk -= 8; 78638 cursor += 8; 78639 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { 78640 return MA_FALSE; 78641 } 78642 bytesRemainingInChunk -= 8; 78643 dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); 78644 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { 78645 return MA_FALSE; 78646 } 78647 bytesRemainingInChunk -= 8; 78648 sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); 78649 if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { 78650 return MA_FALSE; 78651 } 78652 cursor += bytesRemainingInChunk; 78653 } 78654 metadataStartPos = cursor; 78655 isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); 78656 if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { 78657 isProcessingMetadata = MA_FALSE; 78658 } 78659 MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); 78660 if (isProcessingMetadata) { 78661 metadataParser.onRead = pWav->onRead; 78662 metadataParser.onSeek = pWav->onSeek; 78663 metadataParser.pReadSeekUserData = pWav->pUserData; 78664 metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; 78665 } 78666 for (;;) { 78667 ma_dr_wav_chunk_header header; 78668 ma_uint64 chunkSize; 78669 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); 78670 if (result != MA_SUCCESS) { 78671 break; 78672 } 78673 chunkSize = header.sizeInBytes; 78674 if (!sequential && onChunk != NULL) { 78675 ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); 78676 if (callbackBytesRead > 0) { 78677 if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { 78678 return MA_FALSE; 78679 } 78680 } 78681 } 78682 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || 78683 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { 78684 ma_uint8 fmtData[16]; 78685 foundChunk_fmt = MA_TRUE; 78686 if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { 78687 return MA_FALSE; 78688 } 78689 cursor += sizeof(fmtData); 78690 fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); 78691 fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); 78692 fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); 78693 fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); 78694 fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); 78695 fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); 78696 fmt.extendedSize = 0; 78697 fmt.validBitsPerSample = 0; 78698 fmt.channelMask = 0; 78699 MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); 78700 if (header.sizeInBytes > 16) { 78701 ma_uint8 fmt_cbSize[2]; 78702 int bytesReadSoFar = 0; 78703 if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { 78704 return MA_FALSE; 78705 } 78706 cursor += sizeof(fmt_cbSize); 78707 bytesReadSoFar = 18; 78708 fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); 78709 if (fmt.extendedSize > 0) { 78710 if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { 78711 if (fmt.extendedSize != 22) { 78712 return MA_FALSE; 78713 } 78714 } 78715 if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { 78716 ma_uint8 fmtext[22]; 78717 if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { 78718 return MA_FALSE; 78719 } 78720 fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); 78721 fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); 78722 ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); 78723 } else { 78724 if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { 78725 return MA_FALSE; 78726 } 78727 } 78728 cursor += fmt.extendedSize; 78729 bytesReadSoFar += fmt.extendedSize; 78730 } 78731 if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { 78732 return MA_FALSE; 78733 } 78734 cursor += (header.sizeInBytes - bytesReadSoFar); 78735 } 78736 if (header.paddingSize > 0) { 78737 if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { 78738 break; 78739 } 78740 cursor += header.paddingSize; 78741 } 78742 continue; 78743 } 78744 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || 78745 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { 78746 foundChunk_data = MA_TRUE; 78747 pWav->dataChunkDataPos = cursor; 78748 if (pWav->container != ma_dr_wav_container_rf64) { 78749 dataChunkSize = chunkSize; 78750 } 78751 if (sequential || !isProcessingMetadata) { 78752 break; 78753 } else { 78754 chunkSize += header.paddingSize; 78755 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { 78756 break; 78757 } 78758 cursor += chunkSize; 78759 continue; 78760 } 78761 } 78762 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || 78763 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { 78764 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { 78765 ma_uint8 sampleCount[4]; 78766 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { 78767 return MA_FALSE; 78768 } 78769 chunkSize -= 4; 78770 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 78771 sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); 78772 } else { 78773 sampleCountFromFactChunk = 0; 78774 } 78775 } else if (pWav->container == ma_dr_wav_container_w64) { 78776 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { 78777 return MA_FALSE; 78778 } 78779 chunkSize -= 8; 78780 } else if (pWav->container == ma_dr_wav_container_rf64) { 78781 } 78782 chunkSize += header.paddingSize; 78783 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { 78784 break; 78785 } 78786 cursor += chunkSize; 78787 continue; 78788 } 78789 if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { 78790 ma_uint8 commData[24]; 78791 ma_uint32 commDataBytesToRead; 78792 ma_uint16 channels; 78793 ma_uint32 frameCount; 78794 ma_uint16 sampleSizeInBits; 78795 ma_int64 sampleRate; 78796 ma_uint16 compressionFormat; 78797 foundChunk_fmt = MA_TRUE; 78798 if (isAIFCFormType) { 78799 commDataBytesToRead = 24; 78800 if (header.sizeInBytes < commDataBytesToRead) { 78801 return MA_FALSE; 78802 } 78803 } else { 78804 commDataBytesToRead = 18; 78805 if (header.sizeInBytes != commDataBytesToRead) { 78806 return MA_FALSE; 78807 } 78808 } 78809 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { 78810 return MA_FALSE; 78811 } 78812 channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); 78813 frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); 78814 sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); 78815 sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); 78816 if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { 78817 return MA_FALSE; 78818 } 78819 if (isAIFCFormType) { 78820 const ma_uint8* type = commData + 18; 78821 if (ma_dr_wav_fourcc_equal(type, "NONE")) { 78822 compressionFormat = MA_DR_WAVE_FORMAT_PCM; 78823 } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { 78824 compressionFormat = MA_DR_WAVE_FORMAT_PCM; 78825 if (sampleSizeInBits == 8) { 78826 pWav->aiff.isUnsigned = MA_TRUE; 78827 } 78828 } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { 78829 compressionFormat = MA_DR_WAVE_FORMAT_PCM; 78830 pWav->aiff.isLE = MA_TRUE; 78831 } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { 78832 compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; 78833 } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { 78834 compressionFormat = MA_DR_WAVE_FORMAT_ALAW; 78835 } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { 78836 compressionFormat = MA_DR_WAVE_FORMAT_MULAW; 78837 } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { 78838 compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; 78839 sampleSizeInBits = 4; 78840 return MA_FALSE; 78841 } else { 78842 return MA_FALSE; 78843 } 78844 } else { 78845 compressionFormat = MA_DR_WAVE_FORMAT_PCM; 78846 } 78847 aiffFrameCount = frameCount; 78848 fmt.formatTag = compressionFormat; 78849 fmt.channels = channels; 78850 fmt.sampleRate = (ma_uint32)sampleRate; 78851 fmt.bitsPerSample = sampleSizeInBits; 78852 fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); 78853 fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; 78854 if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 78855 fmt.blockAlign = 34 * fmt.channels; 78856 } 78857 if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { 78858 if (fmt.bitsPerSample > 8) { 78859 fmt.bitsPerSample = 8; 78860 fmt.blockAlign = fmt.channels; 78861 } 78862 } 78863 fmt.bitsPerSample += (fmt.bitsPerSample & 7); 78864 if (isAIFCFormType) { 78865 if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { 78866 return MA_FALSE; 78867 } 78868 cursor += (chunkSize - commDataBytesToRead); 78869 } 78870 continue; 78871 } 78872 if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { 78873 ma_uint8 offsetAndBlockSizeData[8]; 78874 ma_uint32 offset; 78875 foundChunk_data = MA_TRUE; 78876 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { 78877 return MA_FALSE; 78878 } 78879 offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); 78880 if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { 78881 return MA_FALSE; 78882 } 78883 cursor += offset; 78884 pWav->dataChunkDataPos = cursor; 78885 dataChunkSize = chunkSize; 78886 if (sequential || !isProcessingMetadata) { 78887 break; 78888 } else { 78889 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { 78890 break; 78891 } 78892 cursor += chunkSize; 78893 continue; 78894 } 78895 } 78896 if (isProcessingMetadata) { 78897 ma_uint64 metadataBytesRead; 78898 metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); 78899 MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); 78900 if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { 78901 break; 78902 } 78903 } 78904 chunkSize += header.paddingSize; 78905 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { 78906 break; 78907 } 78908 cursor += chunkSize; 78909 } 78910 if (!foundChunk_fmt || !foundChunk_data) { 78911 return MA_FALSE; 78912 } 78913 if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || 78914 (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || 78915 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || 78916 fmt.blockAlign == 0) { 78917 return MA_FALSE; 78918 } 78919 translatedFormatTag = fmt.formatTag; 78920 if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { 78921 translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); 78922 } 78923 if (!sequential) { 78924 if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { 78925 return MA_FALSE; 78926 } 78927 cursor = pWav->dataChunkDataPos; 78928 } 78929 if (isProcessingMetadata && metadataParser.metadataCount > 0) { 78930 if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { 78931 return MA_FALSE; 78932 } 78933 result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); 78934 if (result != MA_SUCCESS) { 78935 return MA_FALSE; 78936 } 78937 metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; 78938 for (;;) { 78939 ma_dr_wav_chunk_header header; 78940 ma_uint64 metadataBytesRead; 78941 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); 78942 if (result != MA_SUCCESS) { 78943 break; 78944 } 78945 metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); 78946 if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { 78947 ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); 78948 return MA_FALSE; 78949 } 78950 } 78951 pWav->pMetadata = metadataParser.pMetadata; 78952 pWav->metadataCount = metadataParser.metadataCount; 78953 } 78954 if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { 78955 dataChunkSize = 0; 78956 for (;;) { 78957 ma_uint8 temp[4096]; 78958 size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); 78959 dataChunkSize += bytesRead; 78960 if (bytesRead < sizeof(temp)) { 78961 break; 78962 } 78963 } 78964 } 78965 if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { 78966 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); 78967 return MA_FALSE; 78968 } 78969 pWav->fmt = fmt; 78970 pWav->sampleRate = fmt.sampleRate; 78971 pWav->channels = fmt.channels; 78972 pWav->bitsPerSample = fmt.bitsPerSample; 78973 pWav->bytesRemaining = dataChunkSize; 78974 pWav->translatedFormatTag = translatedFormatTag; 78975 pWav->dataChunkDataSize = dataChunkSize; 78976 if (sampleCountFromFactChunk != 0) { 78977 pWav->totalPCMFrameCount = sampleCountFromFactChunk; 78978 } else if (aiffFrameCount != 0) { 78979 pWav->totalPCMFrameCount = aiffFrameCount; 78980 } else { 78981 ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 78982 if (bytesPerFrame == 0) { 78983 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); 78984 return MA_FALSE; 78985 } 78986 pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; 78987 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 78988 ma_uint64 totalBlockHeaderSizeInBytes; 78989 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; 78990 if ((blockCount * fmt.blockAlign) < dataChunkSize) { 78991 blockCount += 1; 78992 } 78993 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); 78994 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; 78995 } 78996 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 78997 ma_uint64 totalBlockHeaderSizeInBytes; 78998 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; 78999 if ((blockCount * fmt.blockAlign) < dataChunkSize) { 79000 blockCount += 1; 79001 } 79002 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); 79003 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; 79004 pWav->totalPCMFrameCount += blockCount; 79005 } 79006 } 79007 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 79008 if (pWav->channels > 2) { 79009 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); 79010 return MA_FALSE; 79011 } 79012 } 79013 if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { 79014 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); 79015 return MA_FALSE; 79016 } 79017 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 79018 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 79019 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; 79020 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; 79021 } 79022 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 79023 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; 79024 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; 79025 } 79026 #endif 79027 return MA_TRUE; 79028 } 79029 MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 79030 { 79031 return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); 79032 } 79033 MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79034 { 79035 if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { 79036 return MA_FALSE; 79037 } 79038 return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); 79039 } 79040 MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79041 { 79042 if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { 79043 return MA_FALSE; 79044 } 79045 return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); 79046 } 79047 MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) 79048 { 79049 ma_dr_wav_metadata *result = pWav->pMetadata; 79050 pWav->pMetadata = NULL; 79051 pWav->metadataCount = 0; 79052 return result; 79053 } 79054 MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) 79055 { 79056 MA_DR_WAV_ASSERT(pWav != NULL); 79057 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79058 return pWav->onWrite(pWav->pUserData, pData, dataSize); 79059 } 79060 MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) 79061 { 79062 MA_DR_WAV_ASSERT(pWav != NULL); 79063 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79064 return pWav->onWrite(pWav->pUserData, &byte, 1); 79065 } 79066 MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) 79067 { 79068 MA_DR_WAV_ASSERT(pWav != NULL); 79069 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79070 if (!ma_dr_wav__is_little_endian()) { 79071 value = ma_dr_wav__bswap16(value); 79072 } 79073 return ma_dr_wav__write(pWav, &value, 2); 79074 } 79075 MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) 79076 { 79077 MA_DR_WAV_ASSERT(pWav != NULL); 79078 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79079 if (!ma_dr_wav__is_little_endian()) { 79080 value = ma_dr_wav__bswap32(value); 79081 } 79082 return ma_dr_wav__write(pWav, &value, 4); 79083 } 79084 MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) 79085 { 79086 MA_DR_WAV_ASSERT(pWav != NULL); 79087 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79088 if (!ma_dr_wav__is_little_endian()) { 79089 value = ma_dr_wav__bswap64(value); 79090 } 79091 return ma_dr_wav__write(pWav, &value, 8); 79092 } 79093 MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) 79094 { 79095 union { 79096 ma_uint32 u32; 79097 float f32; 79098 } u; 79099 MA_DR_WAV_ASSERT(pWav != NULL); 79100 MA_DR_WAV_ASSERT(pWav->onWrite != NULL); 79101 u.f32 = value; 79102 if (!ma_dr_wav__is_little_endian()) { 79103 u.u32 = ma_dr_wav__bswap32(u.u32); 79104 } 79105 return ma_dr_wav__write(pWav, &u.u32, 4); 79106 } 79107 MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) 79108 { 79109 if (pWav == NULL) { 79110 return dataSize; 79111 } 79112 return ma_dr_wav__write(pWav, pData, dataSize); 79113 } 79114 MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) 79115 { 79116 if (pWav == NULL) { 79117 return 1; 79118 } 79119 return ma_dr_wav__write_byte(pWav, byte); 79120 } 79121 MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) 79122 { 79123 if (pWav == NULL) { 79124 return 2; 79125 } 79126 return ma_dr_wav__write_u16ne_to_le(pWav, value); 79127 } 79128 MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) 79129 { 79130 if (pWav == NULL) { 79131 return 4; 79132 } 79133 return ma_dr_wav__write_u32ne_to_le(pWav, value); 79134 } 79135 #if 0 79136 MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) 79137 { 79138 if (pWav == NULL) { 79139 return 8; 79140 } 79141 return ma_dr_wav__write_u64ne_to_le(pWav, value); 79142 } 79143 #endif 79144 MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) 79145 { 79146 if (pWav == NULL) { 79147 return 4; 79148 } 79149 return ma_dr_wav__write_f32ne_to_le(pWav, value); 79150 } 79151 MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) 79152 { 79153 size_t len; 79154 if (pWav == NULL) { 79155 return bufFixedSize; 79156 } 79157 len = ma_dr_wav__strlen_clamped(str, bufFixedSize); 79158 ma_dr_wav__write_or_count(pWav, str, len); 79159 if (len < bufFixedSize) { 79160 size_t i; 79161 for (i = 0; i < bufFixedSize - len; ++i) { 79162 ma_dr_wav__write_byte(pWav, 0); 79163 } 79164 } 79165 return bufFixedSize; 79166 } 79167 MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) 79168 { 79169 size_t bytesWritten = 0; 79170 ma_bool32 hasListAdtl = MA_FALSE; 79171 ma_bool32 hasListInfo = MA_FALSE; 79172 ma_uint32 iMetadata; 79173 if (pMetadatas == NULL || metadataCount == 0) { 79174 return 0; 79175 } 79176 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { 79177 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; 79178 ma_uint32 chunkSize = 0; 79179 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { 79180 hasListInfo = MA_TRUE; 79181 } 79182 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { 79183 hasListAdtl = MA_TRUE; 79184 } 79185 switch (pMetadata->type) { 79186 case ma_dr_wav_metadata_type_smpl: 79187 { 79188 ma_uint32 iLoop; 79189 chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; 79190 bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); 79191 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79192 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); 79193 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); 79194 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); 79195 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); 79196 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); 79197 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); 79198 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); 79199 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); 79200 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); 79201 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { 79202 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); 79203 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); 79204 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); 79205 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); 79206 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); 79207 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); 79208 } 79209 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { 79210 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); 79211 } 79212 } break; 79213 case ma_dr_wav_metadata_type_inst: 79214 { 79215 chunkSize = MA_DR_WAV_INST_BYTES; 79216 bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); 79217 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79218 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); 79219 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); 79220 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); 79221 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); 79222 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); 79223 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); 79224 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); 79225 } break; 79226 case ma_dr_wav_metadata_type_cue: 79227 { 79228 ma_uint32 iCuePoint; 79229 chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; 79230 bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); 79231 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79232 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); 79233 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { 79234 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); 79235 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); 79236 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); 79237 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); 79238 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); 79239 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); 79240 } 79241 } break; 79242 case ma_dr_wav_metadata_type_acid: 79243 { 79244 chunkSize = MA_DR_WAV_ACID_BYTES; 79245 bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); 79246 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79247 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); 79248 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); 79249 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); 79250 bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); 79251 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); 79252 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); 79253 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); 79254 bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); 79255 } break; 79256 case ma_dr_wav_metadata_type_bext: 79257 { 79258 char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; 79259 ma_uint32 timeReferenceLow; 79260 ma_uint32 timeReferenceHigh; 79261 chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; 79262 bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); 79263 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79264 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); 79265 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); 79266 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); 79267 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); 79268 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); 79269 timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); 79270 timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); 79271 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); 79272 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); 79273 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); 79274 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); 79275 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); 79276 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); 79277 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); 79278 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); 79279 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); 79280 MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); 79281 bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); 79282 if (pMetadata->data.bext.codingHistorySize > 0) { 79283 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); 79284 } 79285 } break; 79286 case ma_dr_wav_metadata_type_unknown: 79287 { 79288 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { 79289 chunkSize = pMetadata->data.unknown.dataSizeInBytes; 79290 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); 79291 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79292 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); 79293 } 79294 } break; 79295 default: break; 79296 } 79297 if ((chunkSize % 2) != 0) { 79298 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); 79299 } 79300 } 79301 if (hasListInfo) { 79302 ma_uint32 chunkSize = 4; 79303 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { 79304 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; 79305 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { 79306 chunkSize += 8; 79307 chunkSize += pMetadata->data.infoText.stringLength + 1; 79308 } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { 79309 chunkSize += 8; 79310 chunkSize += pMetadata->data.unknown.dataSizeInBytes; 79311 } 79312 if ((chunkSize % 2) != 0) { 79313 chunkSize += 1; 79314 } 79315 } 79316 bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); 79317 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79318 bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); 79319 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { 79320 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; 79321 ma_uint32 subchunkSize = 0; 79322 if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { 79323 const char* pID = NULL; 79324 switch (pMetadata->type) { 79325 case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; 79326 case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; 79327 case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; 79328 case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; 79329 case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; 79330 case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; 79331 case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; 79332 case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; 79333 case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; 79334 default: break; 79335 } 79336 MA_DR_WAV_ASSERT(pID != NULL); 79337 if (pMetadata->data.infoText.stringLength) { 79338 subchunkSize = pMetadata->data.infoText.stringLength + 1; 79339 bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); 79340 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); 79341 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); 79342 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); 79343 } 79344 } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { 79345 if (pMetadata->data.unknown.dataSizeInBytes) { 79346 subchunkSize = pMetadata->data.unknown.dataSizeInBytes; 79347 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); 79348 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); 79349 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); 79350 } 79351 } 79352 if ((subchunkSize % 2) != 0) { 79353 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); 79354 } 79355 } 79356 } 79357 if (hasListAdtl) { 79358 ma_uint32 chunkSize = 4; 79359 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { 79360 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; 79361 switch (pMetadata->type) 79362 { 79363 case ma_dr_wav_metadata_type_list_label: 79364 case ma_dr_wav_metadata_type_list_note: 79365 { 79366 chunkSize += 8; 79367 chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; 79368 if (pMetadata->data.labelOrNote.stringLength > 0) { 79369 chunkSize += pMetadata->data.labelOrNote.stringLength + 1; 79370 } 79371 } break; 79372 case ma_dr_wav_metadata_type_list_labelled_cue_region: 79373 { 79374 chunkSize += 8; 79375 chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; 79376 if (pMetadata->data.labelledCueRegion.stringLength > 0) { 79377 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; 79378 } 79379 } break; 79380 case ma_dr_wav_metadata_type_unknown: 79381 { 79382 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { 79383 chunkSize += 8; 79384 chunkSize += pMetadata->data.unknown.dataSizeInBytes; 79385 } 79386 } break; 79387 default: break; 79388 } 79389 if ((chunkSize % 2) != 0) { 79390 chunkSize += 1; 79391 } 79392 } 79393 bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); 79394 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); 79395 bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); 79396 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { 79397 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; 79398 ma_uint32 subchunkSize = 0; 79399 switch (pMetadata->type) 79400 { 79401 case ma_dr_wav_metadata_type_list_label: 79402 case ma_dr_wav_metadata_type_list_note: 79403 { 79404 if (pMetadata->data.labelOrNote.stringLength > 0) { 79405 const char *pID = NULL; 79406 if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { 79407 pID = "labl"; 79408 } 79409 else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { 79410 pID = "note"; 79411 } 79412 MA_DR_WAV_ASSERT(pID != NULL); 79413 MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); 79414 subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; 79415 bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); 79416 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; 79417 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); 79418 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); 79419 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); 79420 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); 79421 } 79422 } break; 79423 case ma_dr_wav_metadata_type_list_labelled_cue_region: 79424 { 79425 subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; 79426 bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); 79427 if (pMetadata->data.labelledCueRegion.stringLength > 0) { 79428 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; 79429 } 79430 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); 79431 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); 79432 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); 79433 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); 79434 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); 79435 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); 79436 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); 79437 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); 79438 if (pMetadata->data.labelledCueRegion.stringLength > 0) { 79439 MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); 79440 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); 79441 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); 79442 } 79443 } break; 79444 case ma_dr_wav_metadata_type_unknown: 79445 { 79446 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { 79447 subchunkSize = pMetadata->data.unknown.dataSizeInBytes; 79448 MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); 79449 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); 79450 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); 79451 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); 79452 } 79453 } break; 79454 default: break; 79455 } 79456 if ((subchunkSize % 2) != 0) { 79457 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); 79458 } 79459 } 79460 } 79461 MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); 79462 return bytesWritten; 79463 } 79464 MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) 79465 { 79466 ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); 79467 if (chunkSize > 0xFFFFFFFFUL) { 79468 chunkSize = 0xFFFFFFFFUL; 79469 } 79470 return (ma_uint32)chunkSize; 79471 } 79472 MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) 79473 { 79474 if (dataChunkSize <= 0xFFFFFFFFUL) { 79475 return (ma_uint32)dataChunkSize; 79476 } else { 79477 return 0xFFFFFFFFUL; 79478 } 79479 } 79480 MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) 79481 { 79482 ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); 79483 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; 79484 } 79485 MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) 79486 { 79487 return 24 + dataChunkSize; 79488 } 79489 MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) 79490 { 79491 ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); 79492 if (chunkSize > 0xFFFFFFFFUL) { 79493 chunkSize = 0xFFFFFFFFUL; 79494 } 79495 return chunkSize; 79496 } 79497 MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) 79498 { 79499 return dataChunkSize; 79500 } 79501 MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 79502 { 79503 if (pWav == NULL || onWrite == NULL) { 79504 return MA_FALSE; 79505 } 79506 if (!isSequential && onSeek == NULL) { 79507 return MA_FALSE; 79508 } 79509 if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { 79510 return MA_FALSE; 79511 } 79512 if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 79513 return MA_FALSE; 79514 } 79515 MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); 79516 pWav->onWrite = onWrite; 79517 pWav->onSeek = onSeek; 79518 pWav->pUserData = pUserData; 79519 pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); 79520 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { 79521 return MA_FALSE; 79522 } 79523 pWav->fmt.formatTag = (ma_uint16)pFormat->format; 79524 pWav->fmt.channels = (ma_uint16)pFormat->channels; 79525 pWav->fmt.sampleRate = pFormat->sampleRate; 79526 pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); 79527 pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); 79528 pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; 79529 pWav->fmt.extendedSize = 0; 79530 pWav->isSequentialWrite = isSequential; 79531 return MA_TRUE; 79532 } 79533 MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) 79534 { 79535 size_t runningPos = 0; 79536 ma_uint64 initialDataChunkSize = 0; 79537 ma_uint64 chunkSizeFMT; 79538 if (pWav->isSequentialWrite) { 79539 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; 79540 if (pFormat->container == ma_dr_wav_container_riff) { 79541 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { 79542 return MA_FALSE; 79543 } 79544 } 79545 } 79546 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; 79547 if (pFormat->container == ma_dr_wav_container_riff) { 79548 ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; 79549 runningPos += ma_dr_wav__write(pWav, "RIFF", 4); 79550 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); 79551 runningPos += ma_dr_wav__write(pWav, "WAVE", 4); 79552 } else if (pFormat->container == ma_dr_wav_container_w64) { 79553 ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; 79554 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); 79555 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); 79556 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); 79557 } else if (pFormat->container == ma_dr_wav_container_rf64) { 79558 runningPos += ma_dr_wav__write(pWav, "RF64", 4); 79559 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); 79560 runningPos += ma_dr_wav__write(pWav, "WAVE", 4); 79561 } else { 79562 return MA_FALSE; 79563 } 79564 if (pFormat->container == ma_dr_wav_container_rf64) { 79565 ma_uint32 initialds64ChunkSize = 28; 79566 ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; 79567 runningPos += ma_dr_wav__write(pWav, "ds64", 4); 79568 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); 79569 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); 79570 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); 79571 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); 79572 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); 79573 } 79574 if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { 79575 chunkSizeFMT = 16; 79576 runningPos += ma_dr_wav__write(pWav, "fmt ", 4); 79577 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); 79578 } else if (pFormat->container == ma_dr_wav_container_w64) { 79579 chunkSizeFMT = 40; 79580 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); 79581 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); 79582 } 79583 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); 79584 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); 79585 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); 79586 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); 79587 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); 79588 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); 79589 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { 79590 runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); 79591 } 79592 pWav->dataChunkDataPos = runningPos; 79593 if (pFormat->container == ma_dr_wav_container_riff) { 79594 ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; 79595 runningPos += ma_dr_wav__write(pWav, "data", 4); 79596 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); 79597 } else if (pFormat->container == ma_dr_wav_container_w64) { 79598 ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; 79599 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); 79600 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); 79601 } else if (pFormat->container == ma_dr_wav_container_rf64) { 79602 runningPos += ma_dr_wav__write(pWav, "data", 4); 79603 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); 79604 } 79605 pWav->container = pFormat->container; 79606 pWav->channels = (ma_uint16)pFormat->channels; 79607 pWav->sampleRate = pFormat->sampleRate; 79608 pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; 79609 pWav->translatedFormatTag = (ma_uint16)pFormat->format; 79610 pWav->dataChunkDataPos = runningPos; 79611 return MA_TRUE; 79612 } 79613 MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 79614 { 79615 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { 79616 return MA_FALSE; 79617 } 79618 return ma_dr_wav_init_write__internal(pWav, pFormat, 0); 79619 } 79620 MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 79621 { 79622 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { 79623 return MA_FALSE; 79624 } 79625 return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); 79626 } 79627 MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 79628 { 79629 if (pFormat == NULL) { 79630 return MA_FALSE; 79631 } 79632 return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); 79633 } 79634 MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) 79635 { 79636 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { 79637 return MA_FALSE; 79638 } 79639 pWav->pMetadata = pMetadata; 79640 pWav->metadataCount = metadataCount; 79641 return ma_dr_wav_init_write__internal(pWav, pFormat, 0); 79642 } 79643 MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) 79644 { 79645 ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); 79646 ma_uint64 riffChunkSizeBytes; 79647 ma_uint64 fileSizeBytes = 0; 79648 if (pFormat->container == ma_dr_wav_container_riff) { 79649 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); 79650 fileSizeBytes = (8 + riffChunkSizeBytes); 79651 } else if (pFormat->container == ma_dr_wav_container_w64) { 79652 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); 79653 fileSizeBytes = riffChunkSizeBytes; 79654 } else if (pFormat->container == ma_dr_wav_container_rf64) { 79655 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); 79656 fileSizeBytes = (8 + riffChunkSizeBytes); 79657 } 79658 return fileSizeBytes; 79659 } 79660 #ifndef MA_DR_WAV_NO_STDIO 79661 MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) 79662 { 79663 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); 79664 } 79665 MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) 79666 { 79667 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); 79668 } 79669 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) 79670 { 79671 return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; 79672 } 79673 MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) 79674 { 79675 return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); 79676 } 79677 MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79678 { 79679 ma_bool32 result; 79680 result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 79681 if (result != MA_TRUE) { 79682 fclose(pFile); 79683 return result; 79684 } 79685 result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); 79686 if (result != MA_TRUE) { 79687 fclose(pFile); 79688 return result; 79689 } 79690 return MA_TRUE; 79691 } 79692 MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79693 { 79694 FILE* pFile; 79695 if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { 79696 return MA_FALSE; 79697 } 79698 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); 79699 } 79700 #ifndef MA_DR_WAV_NO_WCHAR 79701 MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) 79702 { 79703 return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); 79704 } 79705 MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79706 { 79707 FILE* pFile; 79708 if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 79709 return MA_FALSE; 79710 } 79711 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); 79712 } 79713 #endif 79714 MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79715 { 79716 FILE* pFile; 79717 if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { 79718 return MA_FALSE; 79719 } 79720 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); 79721 } 79722 #ifndef MA_DR_WAV_NO_WCHAR 79723 MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79724 { 79725 FILE* pFile; 79726 if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 79727 return MA_FALSE; 79728 } 79729 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); 79730 } 79731 #endif 79732 MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) 79733 { 79734 ma_bool32 result; 79735 result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 79736 if (result != MA_TRUE) { 79737 fclose(pFile); 79738 return result; 79739 } 79740 result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); 79741 if (result != MA_TRUE) { 79742 fclose(pFile); 79743 return result; 79744 } 79745 return MA_TRUE; 79746 } 79747 MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) 79748 { 79749 FILE* pFile; 79750 if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { 79751 return MA_FALSE; 79752 } 79753 return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); 79754 } 79755 #ifndef MA_DR_WAV_NO_WCHAR 79756 MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) 79757 { 79758 FILE* pFile; 79759 if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { 79760 return MA_FALSE; 79761 } 79762 return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); 79763 } 79764 #endif 79765 MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) 79766 { 79767 return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); 79768 } 79769 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) 79770 { 79771 return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); 79772 } 79773 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 79774 { 79775 if (pFormat == NULL) { 79776 return MA_FALSE; 79777 } 79778 return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); 79779 } 79780 #ifndef MA_DR_WAV_NO_WCHAR 79781 MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) 79782 { 79783 return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); 79784 } 79785 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) 79786 { 79787 return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); 79788 } 79789 MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 79790 { 79791 if (pFormat == NULL) { 79792 return MA_FALSE; 79793 } 79794 return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); 79795 } 79796 #endif 79797 #endif 79798 MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) 79799 { 79800 ma_dr_wav* pWav = (ma_dr_wav*)pUserData; 79801 size_t bytesRemaining; 79802 MA_DR_WAV_ASSERT(pWav != NULL); 79803 MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); 79804 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; 79805 if (bytesToRead > bytesRemaining) { 79806 bytesToRead = bytesRemaining; 79807 } 79808 if (bytesToRead > 0) { 79809 MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); 79810 pWav->memoryStream.currentReadPos += bytesToRead; 79811 } 79812 return bytesToRead; 79813 } 79814 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) 79815 { 79816 ma_dr_wav* pWav = (ma_dr_wav*)pUserData; 79817 MA_DR_WAV_ASSERT(pWav != NULL); 79818 if (origin == ma_dr_wav_seek_origin_current) { 79819 if (offset > 0) { 79820 if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { 79821 return MA_FALSE; 79822 } 79823 } else { 79824 if (pWav->memoryStream.currentReadPos < (size_t)-offset) { 79825 return MA_FALSE; 79826 } 79827 } 79828 pWav->memoryStream.currentReadPos += offset; 79829 } else { 79830 if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { 79831 pWav->memoryStream.currentReadPos = offset; 79832 } else { 79833 return MA_FALSE; 79834 } 79835 } 79836 return MA_TRUE; 79837 } 79838 MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) 79839 { 79840 ma_dr_wav* pWav = (ma_dr_wav*)pUserData; 79841 size_t bytesRemaining; 79842 MA_DR_WAV_ASSERT(pWav != NULL); 79843 MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); 79844 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; 79845 if (bytesRemaining < bytesToWrite) { 79846 void* pNewData; 79847 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; 79848 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { 79849 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; 79850 } 79851 pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); 79852 if (pNewData == NULL) { 79853 return 0; 79854 } 79855 *pWav->memoryStreamWrite.ppData = pNewData; 79856 pWav->memoryStreamWrite.dataCapacity = newDataCapacity; 79857 } 79858 MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); 79859 pWav->memoryStreamWrite.currentWritePos += bytesToWrite; 79860 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { 79861 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; 79862 } 79863 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; 79864 return bytesToWrite; 79865 } 79866 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) 79867 { 79868 ma_dr_wav* pWav = (ma_dr_wav*)pUserData; 79869 MA_DR_WAV_ASSERT(pWav != NULL); 79870 if (origin == ma_dr_wav_seek_origin_current) { 79871 if (offset > 0) { 79872 if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { 79873 offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); 79874 } 79875 } else { 79876 if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { 79877 offset = -(int)pWav->memoryStreamWrite.currentWritePos; 79878 } 79879 } 79880 pWav->memoryStreamWrite.currentWritePos += offset; 79881 } else { 79882 if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { 79883 pWav->memoryStreamWrite.currentWritePos = offset; 79884 } else { 79885 pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; 79886 } 79887 } 79888 return MA_TRUE; 79889 } 79890 MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) 79891 { 79892 return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); 79893 } 79894 MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79895 { 79896 if (data == NULL || dataSize == 0) { 79897 return MA_FALSE; 79898 } 79899 if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { 79900 return MA_FALSE; 79901 } 79902 pWav->memoryStream.data = (const ma_uint8*)data; 79903 pWav->memoryStream.dataSize = dataSize; 79904 pWav->memoryStream.currentReadPos = 0; 79905 return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); 79906 } 79907 MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) 79908 { 79909 if (data == NULL || dataSize == 0) { 79910 return MA_FALSE; 79911 } 79912 if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { 79913 return MA_FALSE; 79914 } 79915 pWav->memoryStream.data = (const ma_uint8*)data; 79916 pWav->memoryStream.dataSize = dataSize; 79917 pWav->memoryStream.currentReadPos = 0; 79918 return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); 79919 } 79920 MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) 79921 { 79922 if (ppData == NULL || pDataSize == NULL) { 79923 return MA_FALSE; 79924 } 79925 *ppData = NULL; 79926 *pDataSize = 0; 79927 if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { 79928 return MA_FALSE; 79929 } 79930 pWav->memoryStreamWrite.ppData = ppData; 79931 pWav->memoryStreamWrite.pDataSize = pDataSize; 79932 pWav->memoryStreamWrite.dataSize = 0; 79933 pWav->memoryStreamWrite.dataCapacity = 0; 79934 pWav->memoryStreamWrite.currentWritePos = 0; 79935 return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); 79936 } 79937 MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) 79938 { 79939 return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); 79940 } 79941 MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) 79942 { 79943 return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); 79944 } 79945 MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 79946 { 79947 if (pFormat == NULL) { 79948 return MA_FALSE; 79949 } 79950 return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); 79951 } 79952 MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) 79953 { 79954 ma_result result = MA_SUCCESS; 79955 if (pWav == NULL) { 79956 return MA_INVALID_ARGS; 79957 } 79958 if (pWav->onWrite != NULL) { 79959 ma_uint32 paddingSize = 0; 79960 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { 79961 paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); 79962 } else { 79963 paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); 79964 } 79965 if (paddingSize > 0) { 79966 ma_uint64 paddingData = 0; 79967 ma_dr_wav__write(pWav, &paddingData, paddingSize); 79968 } 79969 if (pWav->onSeek && !pWav->isSequentialWrite) { 79970 if (pWav->container == ma_dr_wav_container_riff) { 79971 if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { 79972 ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); 79973 ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); 79974 } 79975 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { 79976 ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); 79977 ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); 79978 } 79979 } else if (pWav->container == ma_dr_wav_container_w64) { 79980 if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { 79981 ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); 79982 ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); 79983 } 79984 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { 79985 ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); 79986 ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); 79987 } 79988 } else if (pWav->container == ma_dr_wav_container_rf64) { 79989 int ds64BodyPos = 12 + 8; 79990 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { 79991 ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); 79992 ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); 79993 } 79994 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { 79995 ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); 79996 ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); 79997 } 79998 } 79999 } 80000 if (pWav->isSequentialWrite) { 80001 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { 80002 result = MA_INVALID_FILE; 80003 } 80004 } 80005 } else { 80006 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); 80007 } 80008 #ifndef MA_DR_WAV_NO_STDIO 80009 if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { 80010 fclose((FILE*)pWav->pUserData); 80011 } 80012 #endif 80013 return result; 80014 } 80015 MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) 80016 { 80017 size_t bytesRead; 80018 ma_uint32 bytesPerFrame; 80019 if (pWav == NULL || bytesToRead == 0) { 80020 return 0; 80021 } 80022 if (bytesToRead > pWav->bytesRemaining) { 80023 bytesToRead = (size_t)pWav->bytesRemaining; 80024 } 80025 if (bytesToRead == 0) { 80026 return 0; 80027 } 80028 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80029 if (bytesPerFrame == 0) { 80030 return 0; 80031 } 80032 if (pBufferOut != NULL) { 80033 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); 80034 } else { 80035 bytesRead = 0; 80036 while (bytesRead < bytesToRead) { 80037 size_t bytesToSeek = (bytesToRead - bytesRead); 80038 if (bytesToSeek > 0x7FFFFFFF) { 80039 bytesToSeek = 0x7FFFFFFF; 80040 } 80041 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { 80042 break; 80043 } 80044 bytesRead += bytesToSeek; 80045 } 80046 while (bytesRead < bytesToRead) { 80047 ma_uint8 buffer[4096]; 80048 size_t bytesSeeked; 80049 size_t bytesToSeek = (bytesToRead - bytesRead); 80050 if (bytesToSeek > sizeof(buffer)) { 80051 bytesToSeek = sizeof(buffer); 80052 } 80053 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); 80054 bytesRead += bytesSeeked; 80055 if (bytesSeeked < bytesToSeek) { 80056 break; 80057 } 80058 } 80059 } 80060 pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; 80061 pWav->bytesRemaining -= bytesRead; 80062 return bytesRead; 80063 } 80064 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) 80065 { 80066 ma_uint32 bytesPerFrame; 80067 ma_uint64 bytesToRead; 80068 ma_uint64 framesRemainingInFile; 80069 if (pWav == NULL || framesToRead == 0) { 80070 return 0; 80071 } 80072 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { 80073 return 0; 80074 } 80075 framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; 80076 if (framesToRead > framesRemainingInFile) { 80077 framesToRead = framesRemainingInFile; 80078 } 80079 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80080 if (bytesPerFrame == 0) { 80081 return 0; 80082 } 80083 bytesToRead = framesToRead * bytesPerFrame; 80084 if (bytesToRead > MA_SIZE_MAX) { 80085 bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; 80086 } 80087 if (bytesToRead == 0) { 80088 return 0; 80089 } 80090 return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; 80091 } 80092 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) 80093 { 80094 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); 80095 if (pBufferOut != NULL) { 80096 ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80097 if (bytesPerFrame == 0) { 80098 return 0; 80099 } 80100 ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); 80101 } 80102 return framesRead; 80103 } 80104 MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) 80105 { 80106 ma_uint64 framesRead = 0; 80107 if (ma_dr_wav_is_container_be(pWav->container)) { 80108 if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { 80109 if (ma_dr_wav__is_little_endian()) { 80110 framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); 80111 } else { 80112 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); 80113 } 80114 goto post_process; 80115 } 80116 } 80117 if (ma_dr_wav__is_little_endian()) { 80118 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); 80119 } else { 80120 framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); 80121 } 80122 post_process: 80123 { 80124 if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { 80125 if (pBufferOut != NULL) { 80126 ma_uint64 iSample; 80127 for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { 80128 ((ma_uint8*)pBufferOut)[iSample] += 128; 80129 } 80130 } 80131 } 80132 } 80133 return framesRead; 80134 } 80135 MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) 80136 { 80137 if (pWav->onWrite != NULL) { 80138 return MA_FALSE; 80139 } 80140 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { 80141 return MA_FALSE; 80142 } 80143 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { 80144 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 80145 MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); 80146 } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 80147 MA_DR_WAV_ZERO_OBJECT(&pWav->ima); 80148 } else { 80149 MA_DR_WAV_ASSERT(MA_FALSE); 80150 } 80151 } 80152 pWav->readCursorInPCMFrames = 0; 80153 pWav->bytesRemaining = pWav->dataChunkDataSize; 80154 return MA_TRUE; 80155 } 80156 MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) 80157 { 80158 if (pWav == NULL || pWav->onSeek == NULL) { 80159 return MA_FALSE; 80160 } 80161 if (pWav->onWrite != NULL) { 80162 return MA_FALSE; 80163 } 80164 if (pWav->totalPCMFrameCount == 0) { 80165 return MA_TRUE; 80166 } 80167 if (targetFrameIndex > pWav->totalPCMFrameCount) { 80168 targetFrameIndex = pWav->totalPCMFrameCount; 80169 } 80170 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { 80171 if (targetFrameIndex < pWav->readCursorInPCMFrames) { 80172 if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { 80173 return MA_FALSE; 80174 } 80175 } 80176 if (targetFrameIndex > pWav->readCursorInPCMFrames) { 80177 ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; 80178 ma_int16 devnull[2048]; 80179 while (offsetInFrames > 0) { 80180 ma_uint64 framesRead = 0; 80181 ma_uint64 framesToRead = offsetInFrames; 80182 if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { 80183 framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; 80184 } 80185 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 80186 framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); 80187 } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 80188 framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); 80189 } else { 80190 MA_DR_WAV_ASSERT(MA_FALSE); 80191 } 80192 if (framesRead != framesToRead) { 80193 return MA_FALSE; 80194 } 80195 offsetInFrames -= framesRead; 80196 } 80197 } 80198 } else { 80199 ma_uint64 totalSizeInBytes; 80200 ma_uint64 currentBytePos; 80201 ma_uint64 targetBytePos; 80202 ma_uint64 offset; 80203 ma_uint32 bytesPerFrame; 80204 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80205 if (bytesPerFrame == 0) { 80206 return MA_FALSE; 80207 } 80208 totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; 80209 currentBytePos = totalSizeInBytes - pWav->bytesRemaining; 80210 targetBytePos = targetFrameIndex * bytesPerFrame; 80211 if (currentBytePos < targetBytePos) { 80212 offset = (targetBytePos - currentBytePos); 80213 } else { 80214 if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { 80215 return MA_FALSE; 80216 } 80217 offset = targetBytePos; 80218 } 80219 while (offset > 0) { 80220 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); 80221 if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { 80222 return MA_FALSE; 80223 } 80224 pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; 80225 pWav->bytesRemaining -= offset32; 80226 offset -= offset32; 80227 } 80228 } 80229 return MA_TRUE; 80230 } 80231 MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) 80232 { 80233 if (pCursor == NULL) { 80234 return MA_INVALID_ARGS; 80235 } 80236 *pCursor = 0; 80237 if (pWav == NULL) { 80238 return MA_INVALID_ARGS; 80239 } 80240 *pCursor = pWav->readCursorInPCMFrames; 80241 return MA_SUCCESS; 80242 } 80243 MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) 80244 { 80245 if (pLength == NULL) { 80246 return MA_INVALID_ARGS; 80247 } 80248 *pLength = 0; 80249 if (pWav == NULL) { 80250 return MA_INVALID_ARGS; 80251 } 80252 *pLength = pWav->totalPCMFrameCount; 80253 return MA_SUCCESS; 80254 } 80255 MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) 80256 { 80257 size_t bytesWritten; 80258 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { 80259 return 0; 80260 } 80261 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); 80262 pWav->dataChunkDataSize += bytesWritten; 80263 return bytesWritten; 80264 } 80265 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) 80266 { 80267 ma_uint64 bytesToWrite; 80268 ma_uint64 bytesWritten; 80269 const ma_uint8* pRunningData; 80270 if (pWav == NULL || framesToWrite == 0 || pData == NULL) { 80271 return 0; 80272 } 80273 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); 80274 if (bytesToWrite > MA_SIZE_MAX) { 80275 return 0; 80276 } 80277 bytesWritten = 0; 80278 pRunningData = (const ma_uint8*)pData; 80279 while (bytesToWrite > 0) { 80280 size_t bytesJustWritten; 80281 ma_uint64 bytesToWriteThisIteration; 80282 bytesToWriteThisIteration = bytesToWrite; 80283 MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); 80284 bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); 80285 if (bytesJustWritten == 0) { 80286 break; 80287 } 80288 bytesToWrite -= bytesJustWritten; 80289 bytesWritten += bytesJustWritten; 80290 pRunningData += bytesJustWritten; 80291 } 80292 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; 80293 } 80294 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) 80295 { 80296 ma_uint64 bytesToWrite; 80297 ma_uint64 bytesWritten; 80298 ma_uint32 bytesPerSample; 80299 const ma_uint8* pRunningData; 80300 if (pWav == NULL || framesToWrite == 0 || pData == NULL) { 80301 return 0; 80302 } 80303 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); 80304 if (bytesToWrite > MA_SIZE_MAX) { 80305 return 0; 80306 } 80307 bytesWritten = 0; 80308 pRunningData = (const ma_uint8*)pData; 80309 bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; 80310 if (bytesPerSample == 0) { 80311 return 0; 80312 } 80313 while (bytesToWrite > 0) { 80314 ma_uint8 temp[4096]; 80315 ma_uint32 sampleCount; 80316 size_t bytesJustWritten; 80317 ma_uint64 bytesToWriteThisIteration; 80318 bytesToWriteThisIteration = bytesToWrite; 80319 MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); 80320 sampleCount = sizeof(temp)/bytesPerSample; 80321 if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { 80322 bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; 80323 } 80324 MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); 80325 ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); 80326 bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); 80327 if (bytesJustWritten == 0) { 80328 break; 80329 } 80330 bytesToWrite -= bytesJustWritten; 80331 bytesWritten += bytesJustWritten; 80332 pRunningData += bytesJustWritten; 80333 } 80334 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; 80335 } 80336 MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) 80337 { 80338 if (ma_dr_wav__is_little_endian()) { 80339 return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); 80340 } else { 80341 return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); 80342 } 80343 } 80344 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80345 { 80346 ma_uint64 totalFramesRead = 0; 80347 MA_DR_WAV_ASSERT(pWav != NULL); 80348 MA_DR_WAV_ASSERT(framesToRead > 0); 80349 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { 80350 MA_DR_WAV_ASSERT(framesToRead > 0); 80351 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { 80352 if (pWav->channels == 1) { 80353 ma_uint8 header[7]; 80354 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { 80355 return totalFramesRead; 80356 } 80357 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); 80358 pWav->msadpcm.predictor[0] = header[0]; 80359 pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); 80360 pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); 80361 pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); 80362 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; 80363 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; 80364 pWav->msadpcm.cachedFrameCount = 2; 80365 } else { 80366 ma_uint8 header[14]; 80367 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { 80368 return totalFramesRead; 80369 } 80370 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); 80371 pWav->msadpcm.predictor[0] = header[0]; 80372 pWav->msadpcm.predictor[1] = header[1]; 80373 pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); 80374 pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); 80375 pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); 80376 pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); 80377 pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); 80378 pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); 80379 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; 80380 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; 80381 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; 80382 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; 80383 pWav->msadpcm.cachedFrameCount = 2; 80384 } 80385 } 80386 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { 80387 if (pBufferOut != NULL) { 80388 ma_uint32 iSample = 0; 80389 for (iSample = 0; iSample < pWav->channels; iSample += 1) { 80390 pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; 80391 } 80392 pBufferOut += pWav->channels; 80393 } 80394 framesToRead -= 1; 80395 totalFramesRead += 1; 80396 pWav->readCursorInPCMFrames += 1; 80397 pWav->msadpcm.cachedFrameCount -= 1; 80398 } 80399 if (framesToRead == 0) { 80400 break; 80401 } 80402 if (pWav->msadpcm.cachedFrameCount == 0) { 80403 if (pWav->msadpcm.bytesRemainingInBlock == 0) { 80404 continue; 80405 } else { 80406 static ma_int32 adaptationTable[] = { 80407 230, 230, 230, 230, 307, 409, 512, 614, 80408 768, 614, 512, 409, 307, 230, 230, 230 80409 }; 80410 static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; 80411 static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; 80412 ma_uint8 nibbles; 80413 ma_int32 nibble0; 80414 ma_int32 nibble1; 80415 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { 80416 return totalFramesRead; 80417 } 80418 pWav->msadpcm.bytesRemainingInBlock -= 1; 80419 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } 80420 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } 80421 if (pWav->channels == 1) { 80422 ma_int32 newSample0; 80423 ma_int32 newSample1; 80424 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; 80425 newSample0 += nibble0 * pWav->msadpcm.delta[0]; 80426 newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); 80427 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; 80428 if (pWav->msadpcm.delta[0] < 16) { 80429 pWav->msadpcm.delta[0] = 16; 80430 } 80431 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; 80432 pWav->msadpcm.prevFrames[0][1] = newSample0; 80433 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; 80434 newSample1 += nibble1 * pWav->msadpcm.delta[0]; 80435 newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); 80436 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; 80437 if (pWav->msadpcm.delta[0] < 16) { 80438 pWav->msadpcm.delta[0] = 16; 80439 } 80440 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; 80441 pWav->msadpcm.prevFrames[0][1] = newSample1; 80442 pWav->msadpcm.cachedFrames[2] = newSample0; 80443 pWav->msadpcm.cachedFrames[3] = newSample1; 80444 pWav->msadpcm.cachedFrameCount = 2; 80445 } else { 80446 ma_int32 newSample0; 80447 ma_int32 newSample1; 80448 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; 80449 newSample0 += nibble0 * pWav->msadpcm.delta[0]; 80450 newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); 80451 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; 80452 if (pWav->msadpcm.delta[0] < 16) { 80453 pWav->msadpcm.delta[0] = 16; 80454 } 80455 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; 80456 pWav->msadpcm.prevFrames[0][1] = newSample0; 80457 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; 80458 newSample1 += nibble1 * pWav->msadpcm.delta[1]; 80459 newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); 80460 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; 80461 if (pWav->msadpcm.delta[1] < 16) { 80462 pWav->msadpcm.delta[1] = 16; 80463 } 80464 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; 80465 pWav->msadpcm.prevFrames[1][1] = newSample1; 80466 pWav->msadpcm.cachedFrames[2] = newSample0; 80467 pWav->msadpcm.cachedFrames[3] = newSample1; 80468 pWav->msadpcm.cachedFrameCount = 1; 80469 } 80470 } 80471 } 80472 } 80473 return totalFramesRead; 80474 } 80475 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80476 { 80477 ma_uint64 totalFramesRead = 0; 80478 ma_uint32 iChannel; 80479 static ma_int32 indexTable[16] = { 80480 -1, -1, -1, -1, 2, 4, 6, 8, 80481 -1, -1, -1, -1, 2, 4, 6, 8 80482 }; 80483 static ma_int32 stepTable[89] = { 80484 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 80485 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 80486 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 80487 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 80488 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 80489 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 80490 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 80491 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 80492 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 80493 }; 80494 MA_DR_WAV_ASSERT(pWav != NULL); 80495 MA_DR_WAV_ASSERT(framesToRead > 0); 80496 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { 80497 MA_DR_WAV_ASSERT(framesToRead > 0); 80498 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { 80499 if (pWav->channels == 1) { 80500 ma_uint8 header[4]; 80501 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { 80502 return totalFramesRead; 80503 } 80504 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); 80505 if (header[2] >= ma_dr_wav_countof(stepTable)) { 80506 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); 80507 pWav->ima.bytesRemainingInBlock = 0; 80508 return totalFramesRead; 80509 } 80510 pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); 80511 pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); 80512 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; 80513 pWav->ima.cachedFrameCount = 1; 80514 } else { 80515 ma_uint8 header[8]; 80516 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { 80517 return totalFramesRead; 80518 } 80519 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); 80520 if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { 80521 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); 80522 pWav->ima.bytesRemainingInBlock = 0; 80523 return totalFramesRead; 80524 } 80525 pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); 80526 pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); 80527 pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); 80528 pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); 80529 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; 80530 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; 80531 pWav->ima.cachedFrameCount = 1; 80532 } 80533 } 80534 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { 80535 if (pBufferOut != NULL) { 80536 ma_uint32 iSample; 80537 for (iSample = 0; iSample < pWav->channels; iSample += 1) { 80538 pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; 80539 } 80540 pBufferOut += pWav->channels; 80541 } 80542 framesToRead -= 1; 80543 totalFramesRead += 1; 80544 pWav->readCursorInPCMFrames += 1; 80545 pWav->ima.cachedFrameCount -= 1; 80546 } 80547 if (framesToRead == 0) { 80548 break; 80549 } 80550 if (pWav->ima.cachedFrameCount == 0) { 80551 if (pWav->ima.bytesRemainingInBlock == 0) { 80552 continue; 80553 } else { 80554 pWav->ima.cachedFrameCount = 8; 80555 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { 80556 ma_uint32 iByte; 80557 ma_uint8 nibbles[4]; 80558 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { 80559 pWav->ima.cachedFrameCount = 0; 80560 return totalFramesRead; 80561 } 80562 pWav->ima.bytesRemainingInBlock -= 4; 80563 for (iByte = 0; iByte < 4; ++iByte) { 80564 ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); 80565 ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); 80566 ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; 80567 ma_int32 predictor = pWav->ima.predictor[iChannel]; 80568 ma_int32 diff = step >> 3; 80569 if (nibble0 & 1) diff += step >> 2; 80570 if (nibble0 & 2) diff += step >> 1; 80571 if (nibble0 & 4) diff += step; 80572 if (nibble0 & 8) diff = -diff; 80573 predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); 80574 pWav->ima.predictor[iChannel] = predictor; 80575 pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); 80576 pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; 80577 step = stepTable[pWav->ima.stepIndex[iChannel]]; 80578 predictor = pWav->ima.predictor[iChannel]; 80579 diff = step >> 3; 80580 if (nibble1 & 1) diff += step >> 2; 80581 if (nibble1 & 2) diff += step >> 1; 80582 if (nibble1 & 4) diff += step; 80583 if (nibble1 & 8) diff = -diff; 80584 predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); 80585 pWav->ima.predictor[iChannel] = predictor; 80586 pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); 80587 pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; 80588 } 80589 } 80590 } 80591 } 80592 } 80593 return totalFramesRead; 80594 } 80595 #ifndef MA_DR_WAV_NO_CONVERSION_API 80596 static unsigned short g_ma_dr_wavAlawTable[256] = { 80597 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 80598 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 80599 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, 80600 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, 80601 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, 80602 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, 80603 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, 80604 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, 80605 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, 80606 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, 80607 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, 80608 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, 80609 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, 80610 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, 80611 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 80612 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 80613 }; 80614 static unsigned short g_ma_dr_wavMulawTable[256] = { 80615 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 80616 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 80617 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, 80618 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, 80619 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, 80620 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, 80621 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, 80622 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, 80623 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, 80624 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, 80625 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, 80626 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, 80627 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, 80628 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, 80629 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 80630 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 80631 }; 80632 static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) 80633 { 80634 return (short)g_ma_dr_wavAlawTable[sampleIn]; 80635 } 80636 static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) 80637 { 80638 return (short)g_ma_dr_wavMulawTable[sampleIn]; 80639 } 80640 MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) 80641 { 80642 size_t i; 80643 if (bytesPerSample == 1) { 80644 ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); 80645 return; 80646 } 80647 if (bytesPerSample == 2) { 80648 for (i = 0; i < totalSampleCount; ++i) { 80649 *pOut++ = ((const ma_int16*)pIn)[i]; 80650 } 80651 return; 80652 } 80653 if (bytesPerSample == 3) { 80654 ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); 80655 return; 80656 } 80657 if (bytesPerSample == 4) { 80658 ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); 80659 return; 80660 } 80661 if (bytesPerSample > 8) { 80662 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); 80663 return; 80664 } 80665 for (i = 0; i < totalSampleCount; ++i) { 80666 ma_uint64 sample = 0; 80667 unsigned int shift = (8 - bytesPerSample) * 8; 80668 unsigned int j; 80669 for (j = 0; j < bytesPerSample; j += 1) { 80670 MA_DR_WAV_ASSERT(j < 8); 80671 sample |= (ma_uint64)(pIn[j]) << shift; 80672 shift += 8; 80673 } 80674 pIn += j; 80675 *pOut++ = (ma_int16)((ma_int64)sample >> 48); 80676 } 80677 } 80678 MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) 80679 { 80680 if (bytesPerSample == 4) { 80681 ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); 80682 return; 80683 } else if (bytesPerSample == 8) { 80684 ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); 80685 return; 80686 } else { 80687 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); 80688 return; 80689 } 80690 } 80691 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80692 { 80693 ma_uint64 totalFramesRead; 80694 ma_uint8 sampleData[4096] = {0}; 80695 ma_uint32 bytesPerFrame; 80696 ma_uint32 bytesPerSample; 80697 ma_uint64 samplesRead; 80698 if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { 80699 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); 80700 } 80701 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80702 if (bytesPerFrame == 0) { 80703 return 0; 80704 } 80705 bytesPerSample = bytesPerFrame / pWav->channels; 80706 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 80707 return 0; 80708 } 80709 totalFramesRead = 0; 80710 while (framesToRead > 0) { 80711 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 80712 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 80713 if (framesRead == 0) { 80714 break; 80715 } 80716 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 80717 samplesRead = framesRead * pWav->channels; 80718 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 80719 MA_DR_WAV_ASSERT(MA_FALSE); 80720 break; 80721 } 80722 ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 80723 pBufferOut += samplesRead; 80724 framesToRead -= framesRead; 80725 totalFramesRead += framesRead; 80726 } 80727 return totalFramesRead; 80728 } 80729 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80730 { 80731 ma_uint64 totalFramesRead; 80732 ma_uint8 sampleData[4096] = {0}; 80733 ma_uint32 bytesPerFrame; 80734 ma_uint32 bytesPerSample; 80735 ma_uint64 samplesRead; 80736 if (pBufferOut == NULL) { 80737 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 80738 } 80739 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80740 if (bytesPerFrame == 0) { 80741 return 0; 80742 } 80743 bytesPerSample = bytesPerFrame / pWav->channels; 80744 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 80745 return 0; 80746 } 80747 totalFramesRead = 0; 80748 while (framesToRead > 0) { 80749 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 80750 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 80751 if (framesRead == 0) { 80752 break; 80753 } 80754 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 80755 samplesRead = framesRead * pWav->channels; 80756 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 80757 MA_DR_WAV_ASSERT(MA_FALSE); 80758 break; 80759 } 80760 ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 80761 pBufferOut += samplesRead; 80762 framesToRead -= framesRead; 80763 totalFramesRead += framesRead; 80764 } 80765 return totalFramesRead; 80766 } 80767 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80768 { 80769 ma_uint64 totalFramesRead; 80770 ma_uint8 sampleData[4096] = {0}; 80771 ma_uint32 bytesPerFrame; 80772 ma_uint32 bytesPerSample; 80773 ma_uint64 samplesRead; 80774 if (pBufferOut == NULL) { 80775 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 80776 } 80777 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80778 if (bytesPerFrame == 0) { 80779 return 0; 80780 } 80781 bytesPerSample = bytesPerFrame / pWav->channels; 80782 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 80783 return 0; 80784 } 80785 totalFramesRead = 0; 80786 while (framesToRead > 0) { 80787 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 80788 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 80789 if (framesRead == 0) { 80790 break; 80791 } 80792 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 80793 samplesRead = framesRead * pWav->channels; 80794 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 80795 MA_DR_WAV_ASSERT(MA_FALSE); 80796 break; 80797 } 80798 ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); 80799 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 80800 { 80801 if (pWav->container == ma_dr_wav_container_aiff) { 80802 ma_uint64 iSample; 80803 for (iSample = 0; iSample < samplesRead; iSample += 1) { 80804 pBufferOut[iSample] = -pBufferOut[iSample]; 80805 } 80806 } 80807 } 80808 #endif 80809 pBufferOut += samplesRead; 80810 framesToRead -= framesRead; 80811 totalFramesRead += framesRead; 80812 } 80813 return totalFramesRead; 80814 } 80815 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80816 { 80817 ma_uint64 totalFramesRead; 80818 ma_uint8 sampleData[4096] = {0}; 80819 ma_uint32 bytesPerFrame; 80820 ma_uint32 bytesPerSample; 80821 ma_uint64 samplesRead; 80822 if (pBufferOut == NULL) { 80823 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 80824 } 80825 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 80826 if (bytesPerFrame == 0) { 80827 return 0; 80828 } 80829 bytesPerSample = bytesPerFrame / pWav->channels; 80830 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 80831 return 0; 80832 } 80833 totalFramesRead = 0; 80834 while (framesToRead > 0) { 80835 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 80836 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 80837 if (framesRead == 0) { 80838 break; 80839 } 80840 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 80841 samplesRead = framesRead * pWav->channels; 80842 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 80843 MA_DR_WAV_ASSERT(MA_FALSE); 80844 break; 80845 } 80846 ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); 80847 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 80848 { 80849 if (pWav->container == ma_dr_wav_container_aiff) { 80850 ma_uint64 iSample; 80851 for (iSample = 0; iSample < samplesRead; iSample += 1) { 80852 pBufferOut[iSample] = -pBufferOut[iSample]; 80853 } 80854 } 80855 } 80856 #endif 80857 pBufferOut += samplesRead; 80858 framesToRead -= framesRead; 80859 totalFramesRead += framesRead; 80860 } 80861 return totalFramesRead; 80862 } 80863 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80864 { 80865 if (pWav == NULL || framesToRead == 0) { 80866 return 0; 80867 } 80868 if (pBufferOut == NULL) { 80869 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 80870 } 80871 if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { 80872 framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; 80873 } 80874 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { 80875 return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); 80876 } 80877 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { 80878 return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); 80879 } 80880 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { 80881 return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); 80882 } 80883 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { 80884 return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); 80885 } 80886 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { 80887 return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); 80888 } 80889 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 80890 return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); 80891 } 80892 return 0; 80893 } 80894 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80895 { 80896 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); 80897 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { 80898 ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); 80899 } 80900 return framesRead; 80901 } 80902 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) 80903 { 80904 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); 80905 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { 80906 ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); 80907 } 80908 return framesRead; 80909 } 80910 MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) 80911 { 80912 int r; 80913 size_t i; 80914 for (i = 0; i < sampleCount; ++i) { 80915 int x = pIn[i]; 80916 r = x << 8; 80917 r = r - 32768; 80918 pOut[i] = (short)r; 80919 } 80920 } 80921 MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) 80922 { 80923 int r; 80924 size_t i; 80925 for (i = 0; i < sampleCount; ++i) { 80926 int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; 80927 r = x >> 8; 80928 pOut[i] = (short)r; 80929 } 80930 } 80931 MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) 80932 { 80933 int r; 80934 size_t i; 80935 for (i = 0; i < sampleCount; ++i) { 80936 int x = pIn[i]; 80937 r = x >> 16; 80938 pOut[i] = (short)r; 80939 } 80940 } 80941 MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) 80942 { 80943 int r; 80944 size_t i; 80945 for (i = 0; i < sampleCount; ++i) { 80946 float x = pIn[i]; 80947 float c; 80948 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); 80949 c = c + 1; 80950 r = (int)(c * 32767.5f); 80951 r = r - 32768; 80952 pOut[i] = (short)r; 80953 } 80954 } 80955 MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) 80956 { 80957 int r; 80958 size_t i; 80959 for (i = 0; i < sampleCount; ++i) { 80960 double x = pIn[i]; 80961 double c; 80962 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); 80963 c = c + 1; 80964 r = (int)(c * 32767.5); 80965 r = r - 32768; 80966 pOut[i] = (short)r; 80967 } 80968 } 80969 MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) 80970 { 80971 size_t i; 80972 for (i = 0; i < sampleCount; ++i) { 80973 pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); 80974 } 80975 } 80976 MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) 80977 { 80978 size_t i; 80979 for (i = 0; i < sampleCount; ++i) { 80980 pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); 80981 } 80982 } 80983 MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) 80984 { 80985 unsigned int i; 80986 if (bytesPerSample == 1) { 80987 ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); 80988 return; 80989 } 80990 if (bytesPerSample == 2) { 80991 ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); 80992 return; 80993 } 80994 if (bytesPerSample == 3) { 80995 ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); 80996 return; 80997 } 80998 if (bytesPerSample == 4) { 80999 ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); 81000 return; 81001 } 81002 if (bytesPerSample > 8) { 81003 MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); 81004 return; 81005 } 81006 for (i = 0; i < sampleCount; ++i) { 81007 ma_uint64 sample = 0; 81008 unsigned int shift = (8 - bytesPerSample) * 8; 81009 unsigned int j; 81010 for (j = 0; j < bytesPerSample; j += 1) { 81011 MA_DR_WAV_ASSERT(j < 8); 81012 sample |= (ma_uint64)(pIn[j]) << shift; 81013 shift += 8; 81014 } 81015 pIn += j; 81016 *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); 81017 } 81018 } 81019 MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) 81020 { 81021 if (bytesPerSample == 4) { 81022 unsigned int i; 81023 for (i = 0; i < sampleCount; ++i) { 81024 *pOut++ = ((const float*)pIn)[i]; 81025 } 81026 return; 81027 } else if (bytesPerSample == 8) { 81028 ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); 81029 return; 81030 } else { 81031 MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); 81032 return; 81033 } 81034 } 81035 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81036 { 81037 ma_uint64 totalFramesRead; 81038 ma_uint8 sampleData[4096] = {0}; 81039 ma_uint32 bytesPerFrame; 81040 ma_uint32 bytesPerSample; 81041 ma_uint64 samplesRead; 81042 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81043 if (bytesPerFrame == 0) { 81044 return 0; 81045 } 81046 bytesPerSample = bytesPerFrame / pWav->channels; 81047 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81048 return 0; 81049 } 81050 totalFramesRead = 0; 81051 while (framesToRead > 0) { 81052 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81053 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81054 if (framesRead == 0) { 81055 break; 81056 } 81057 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81058 samplesRead = framesRead * pWav->channels; 81059 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81060 MA_DR_WAV_ASSERT(MA_FALSE); 81061 break; 81062 } 81063 ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 81064 pBufferOut += samplesRead; 81065 framesToRead -= framesRead; 81066 totalFramesRead += framesRead; 81067 } 81068 return totalFramesRead; 81069 } 81070 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81071 { 81072 ma_uint64 totalFramesRead; 81073 ma_int16 samples16[2048]; 81074 totalFramesRead = 0; 81075 while (framesToRead > 0) { 81076 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); 81077 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); 81078 if (framesRead == 0) { 81079 break; 81080 } 81081 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81082 ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); 81083 pBufferOut += framesRead*pWav->channels; 81084 framesToRead -= framesRead; 81085 totalFramesRead += framesRead; 81086 } 81087 return totalFramesRead; 81088 } 81089 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81090 { 81091 ma_uint64 totalFramesRead; 81092 ma_uint8 sampleData[4096] = {0}; 81093 ma_uint32 bytesPerFrame; 81094 ma_uint32 bytesPerSample; 81095 ma_uint64 samplesRead; 81096 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { 81097 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); 81098 } 81099 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81100 if (bytesPerFrame == 0) { 81101 return 0; 81102 } 81103 bytesPerSample = bytesPerFrame / pWav->channels; 81104 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81105 return 0; 81106 } 81107 totalFramesRead = 0; 81108 while (framesToRead > 0) { 81109 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81110 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81111 if (framesRead == 0) { 81112 break; 81113 } 81114 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81115 samplesRead = framesRead * pWav->channels; 81116 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81117 MA_DR_WAV_ASSERT(MA_FALSE); 81118 break; 81119 } 81120 ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 81121 pBufferOut += samplesRead; 81122 framesToRead -= framesRead; 81123 totalFramesRead += framesRead; 81124 } 81125 return totalFramesRead; 81126 } 81127 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81128 { 81129 ma_uint64 totalFramesRead; 81130 ma_uint8 sampleData[4096] = {0}; 81131 ma_uint32 bytesPerFrame; 81132 ma_uint32 bytesPerSample; 81133 ma_uint64 samplesRead; 81134 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81135 if (bytesPerFrame == 0) { 81136 return 0; 81137 } 81138 bytesPerSample = bytesPerFrame / pWav->channels; 81139 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81140 return 0; 81141 } 81142 totalFramesRead = 0; 81143 while (framesToRead > 0) { 81144 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81145 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81146 if (framesRead == 0) { 81147 break; 81148 } 81149 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81150 samplesRead = framesRead * pWav->channels; 81151 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81152 MA_DR_WAV_ASSERT(MA_FALSE); 81153 break; 81154 } 81155 ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); 81156 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 81157 { 81158 if (pWav->container == ma_dr_wav_container_aiff) { 81159 ma_uint64 iSample; 81160 for (iSample = 0; iSample < samplesRead; iSample += 1) { 81161 pBufferOut[iSample] = -pBufferOut[iSample]; 81162 } 81163 } 81164 } 81165 #endif 81166 pBufferOut += samplesRead; 81167 framesToRead -= framesRead; 81168 totalFramesRead += framesRead; 81169 } 81170 return totalFramesRead; 81171 } 81172 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81173 { 81174 ma_uint64 totalFramesRead; 81175 ma_uint8 sampleData[4096] = {0}; 81176 ma_uint32 bytesPerFrame; 81177 ma_uint32 bytesPerSample; 81178 ma_uint64 samplesRead; 81179 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81180 if (bytesPerFrame == 0) { 81181 return 0; 81182 } 81183 bytesPerSample = bytesPerFrame / pWav->channels; 81184 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81185 return 0; 81186 } 81187 totalFramesRead = 0; 81188 while (framesToRead > 0) { 81189 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81190 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81191 if (framesRead == 0) { 81192 break; 81193 } 81194 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81195 samplesRead = framesRead * pWav->channels; 81196 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81197 MA_DR_WAV_ASSERT(MA_FALSE); 81198 break; 81199 } 81200 ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); 81201 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 81202 { 81203 if (pWav->container == ma_dr_wav_container_aiff) { 81204 ma_uint64 iSample; 81205 for (iSample = 0; iSample < samplesRead; iSample += 1) { 81206 pBufferOut[iSample] = -pBufferOut[iSample]; 81207 } 81208 } 81209 } 81210 #endif 81211 pBufferOut += samplesRead; 81212 framesToRead -= framesRead; 81213 totalFramesRead += framesRead; 81214 } 81215 return totalFramesRead; 81216 } 81217 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81218 { 81219 if (pWav == NULL || framesToRead == 0) { 81220 return 0; 81221 } 81222 if (pBufferOut == NULL) { 81223 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 81224 } 81225 if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { 81226 framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; 81227 } 81228 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { 81229 return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); 81230 } 81231 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 81232 return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); 81233 } 81234 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { 81235 return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); 81236 } 81237 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { 81238 return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); 81239 } 81240 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { 81241 return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); 81242 } 81243 return 0; 81244 } 81245 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81246 { 81247 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); 81248 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { 81249 ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); 81250 } 81251 return framesRead; 81252 } 81253 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) 81254 { 81255 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); 81256 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { 81257 ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); 81258 } 81259 return framesRead; 81260 } 81261 MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) 81262 { 81263 size_t i; 81264 if (pOut == NULL || pIn == NULL) { 81265 return; 81266 } 81267 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 81268 for (i = 0; i < sampleCount; ++i) { 81269 *pOut++ = (pIn[i] / 256.0f) * 2 - 1; 81270 } 81271 #else 81272 for (i = 0; i < sampleCount; ++i) { 81273 float x = pIn[i]; 81274 x = x * 0.00784313725490196078f; 81275 x = x - 1; 81276 *pOut++ = x; 81277 } 81278 #endif 81279 } 81280 MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) 81281 { 81282 size_t i; 81283 if (pOut == NULL || pIn == NULL) { 81284 return; 81285 } 81286 for (i = 0; i < sampleCount; ++i) { 81287 *pOut++ = pIn[i] * 0.000030517578125f; 81288 } 81289 } 81290 MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) 81291 { 81292 size_t i; 81293 if (pOut == NULL || pIn == NULL) { 81294 return; 81295 } 81296 for (i = 0; i < sampleCount; ++i) { 81297 double x; 81298 ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); 81299 ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); 81300 ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); 81301 x = (double)((ma_int32)(a | b | c) >> 8); 81302 *pOut++ = (float)(x * 0.00000011920928955078125); 81303 } 81304 } 81305 MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) 81306 { 81307 size_t i; 81308 if (pOut == NULL || pIn == NULL) { 81309 return; 81310 } 81311 for (i = 0; i < sampleCount; ++i) { 81312 *pOut++ = (float)(pIn[i] / 2147483648.0); 81313 } 81314 } 81315 MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) 81316 { 81317 size_t i; 81318 if (pOut == NULL || pIn == NULL) { 81319 return; 81320 } 81321 for (i = 0; i < sampleCount; ++i) { 81322 *pOut++ = (float)pIn[i]; 81323 } 81324 } 81325 MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) 81326 { 81327 size_t i; 81328 if (pOut == NULL || pIn == NULL) { 81329 return; 81330 } 81331 for (i = 0; i < sampleCount; ++i) { 81332 *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; 81333 } 81334 } 81335 MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) 81336 { 81337 size_t i; 81338 if (pOut == NULL || pIn == NULL) { 81339 return; 81340 } 81341 for (i = 0; i < sampleCount; ++i) { 81342 *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; 81343 } 81344 } 81345 MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) 81346 { 81347 unsigned int i; 81348 if (bytesPerSample == 1) { 81349 ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); 81350 return; 81351 } 81352 if (bytesPerSample == 2) { 81353 ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); 81354 return; 81355 } 81356 if (bytesPerSample == 3) { 81357 ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); 81358 return; 81359 } 81360 if (bytesPerSample == 4) { 81361 for (i = 0; i < totalSampleCount; ++i) { 81362 *pOut++ = ((const ma_int32*)pIn)[i]; 81363 } 81364 return; 81365 } 81366 if (bytesPerSample > 8) { 81367 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); 81368 return; 81369 } 81370 for (i = 0; i < totalSampleCount; ++i) { 81371 ma_uint64 sample = 0; 81372 unsigned int shift = (8 - bytesPerSample) * 8; 81373 unsigned int j; 81374 for (j = 0; j < bytesPerSample; j += 1) { 81375 MA_DR_WAV_ASSERT(j < 8); 81376 sample |= (ma_uint64)(pIn[j]) << shift; 81377 shift += 8; 81378 } 81379 pIn += j; 81380 *pOut++ = (ma_int32)((ma_int64)sample >> 32); 81381 } 81382 } 81383 MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) 81384 { 81385 if (bytesPerSample == 4) { 81386 ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); 81387 return; 81388 } else if (bytesPerSample == 8) { 81389 ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); 81390 return; 81391 } else { 81392 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); 81393 return; 81394 } 81395 } 81396 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81397 { 81398 ma_uint64 totalFramesRead; 81399 ma_uint8 sampleData[4096] = {0}; 81400 ma_uint32 bytesPerFrame; 81401 ma_uint32 bytesPerSample; 81402 ma_uint64 samplesRead; 81403 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { 81404 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); 81405 } 81406 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81407 if (bytesPerFrame == 0) { 81408 return 0; 81409 } 81410 bytesPerSample = bytesPerFrame / pWav->channels; 81411 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81412 return 0; 81413 } 81414 totalFramesRead = 0; 81415 while (framesToRead > 0) { 81416 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81417 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81418 if (framesRead == 0) { 81419 break; 81420 } 81421 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81422 samplesRead = framesRead * pWav->channels; 81423 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81424 MA_DR_WAV_ASSERT(MA_FALSE); 81425 break; 81426 } 81427 ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 81428 pBufferOut += samplesRead; 81429 framesToRead -= framesRead; 81430 totalFramesRead += framesRead; 81431 } 81432 return totalFramesRead; 81433 } 81434 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81435 { 81436 ma_uint64 totalFramesRead = 0; 81437 ma_int16 samples16[2048]; 81438 while (framesToRead > 0) { 81439 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); 81440 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); 81441 if (framesRead == 0) { 81442 break; 81443 } 81444 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81445 ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); 81446 pBufferOut += framesRead*pWav->channels; 81447 framesToRead -= framesRead; 81448 totalFramesRead += framesRead; 81449 } 81450 return totalFramesRead; 81451 } 81452 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81453 { 81454 ma_uint64 totalFramesRead; 81455 ma_uint8 sampleData[4096] = {0}; 81456 ma_uint32 bytesPerFrame; 81457 ma_uint32 bytesPerSample; 81458 ma_uint64 samplesRead; 81459 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81460 if (bytesPerFrame == 0) { 81461 return 0; 81462 } 81463 bytesPerSample = bytesPerFrame / pWav->channels; 81464 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81465 return 0; 81466 } 81467 totalFramesRead = 0; 81468 while (framesToRead > 0) { 81469 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81470 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81471 if (framesRead == 0) { 81472 break; 81473 } 81474 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81475 samplesRead = framesRead * pWav->channels; 81476 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81477 MA_DR_WAV_ASSERT(MA_FALSE); 81478 break; 81479 } 81480 ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); 81481 pBufferOut += samplesRead; 81482 framesToRead -= framesRead; 81483 totalFramesRead += framesRead; 81484 } 81485 return totalFramesRead; 81486 } 81487 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81488 { 81489 ma_uint64 totalFramesRead; 81490 ma_uint8 sampleData[4096] = {0}; 81491 ma_uint32 bytesPerFrame; 81492 ma_uint32 bytesPerSample; 81493 ma_uint64 samplesRead; 81494 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81495 if (bytesPerFrame == 0) { 81496 return 0; 81497 } 81498 bytesPerSample = bytesPerFrame / pWav->channels; 81499 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81500 return 0; 81501 } 81502 totalFramesRead = 0; 81503 while (framesToRead > 0) { 81504 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81505 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81506 if (framesRead == 0) { 81507 break; 81508 } 81509 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81510 samplesRead = framesRead * pWav->channels; 81511 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81512 MA_DR_WAV_ASSERT(MA_FALSE); 81513 break; 81514 } 81515 ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); 81516 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 81517 { 81518 if (pWav->container == ma_dr_wav_container_aiff) { 81519 ma_uint64 iSample; 81520 for (iSample = 0; iSample < samplesRead; iSample += 1) { 81521 pBufferOut[iSample] = -pBufferOut[iSample]; 81522 } 81523 } 81524 } 81525 #endif 81526 pBufferOut += samplesRead; 81527 framesToRead -= framesRead; 81528 totalFramesRead += framesRead; 81529 } 81530 return totalFramesRead; 81531 } 81532 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81533 { 81534 ma_uint64 totalFramesRead; 81535 ma_uint8 sampleData[4096] = {0}; 81536 ma_uint32 bytesPerFrame; 81537 ma_uint32 bytesPerSample; 81538 ma_uint64 samplesRead; 81539 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); 81540 if (bytesPerFrame == 0) { 81541 return 0; 81542 } 81543 bytesPerSample = bytesPerFrame / pWav->channels; 81544 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { 81545 return 0; 81546 } 81547 totalFramesRead = 0; 81548 while (framesToRead > 0) { 81549 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); 81550 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); 81551 if (framesRead == 0) { 81552 break; 81553 } 81554 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); 81555 samplesRead = framesRead * pWav->channels; 81556 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { 81557 MA_DR_WAV_ASSERT(MA_FALSE); 81558 break; 81559 } 81560 ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); 81561 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT 81562 { 81563 if (pWav->container == ma_dr_wav_container_aiff) { 81564 ma_uint64 iSample; 81565 for (iSample = 0; iSample < samplesRead; iSample += 1) { 81566 pBufferOut[iSample] = -pBufferOut[iSample]; 81567 } 81568 } 81569 } 81570 #endif 81571 pBufferOut += samplesRead; 81572 framesToRead -= framesRead; 81573 totalFramesRead += framesRead; 81574 } 81575 return totalFramesRead; 81576 } 81577 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81578 { 81579 if (pWav == NULL || framesToRead == 0) { 81580 return 0; 81581 } 81582 if (pBufferOut == NULL) { 81583 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); 81584 } 81585 if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { 81586 framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; 81587 } 81588 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { 81589 return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); 81590 } 81591 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { 81592 return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); 81593 } 81594 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { 81595 return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); 81596 } 81597 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { 81598 return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); 81599 } 81600 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { 81601 return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); 81602 } 81603 return 0; 81604 } 81605 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81606 { 81607 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); 81608 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { 81609 ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); 81610 } 81611 return framesRead; 81612 } 81613 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) 81614 { 81615 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); 81616 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { 81617 ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); 81618 } 81619 return framesRead; 81620 } 81621 MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) 81622 { 81623 size_t i; 81624 if (pOut == NULL || pIn == NULL) { 81625 return; 81626 } 81627 for (i = 0; i < sampleCount; ++i) { 81628 *pOut++ = ((int)pIn[i] - 128) << 24; 81629 } 81630 } 81631 MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) 81632 { 81633 size_t i; 81634 if (pOut == NULL || pIn == NULL) { 81635 return; 81636 } 81637 for (i = 0; i < sampleCount; ++i) { 81638 *pOut++ = pIn[i] << 16; 81639 } 81640 } 81641 MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) 81642 { 81643 size_t i; 81644 if (pOut == NULL || pIn == NULL) { 81645 return; 81646 } 81647 for (i = 0; i < sampleCount; ++i) { 81648 unsigned int s0 = pIn[i*3 + 0]; 81649 unsigned int s1 = pIn[i*3 + 1]; 81650 unsigned int s2 = pIn[i*3 + 2]; 81651 ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); 81652 *pOut++ = sample32; 81653 } 81654 } 81655 MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) 81656 { 81657 size_t i; 81658 if (pOut == NULL || pIn == NULL) { 81659 return; 81660 } 81661 for (i = 0; i < sampleCount; ++i) { 81662 *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); 81663 } 81664 } 81665 MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) 81666 { 81667 size_t i; 81668 if (pOut == NULL || pIn == NULL) { 81669 return; 81670 } 81671 for (i = 0; i < sampleCount; ++i) { 81672 *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); 81673 } 81674 } 81675 MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) 81676 { 81677 size_t i; 81678 if (pOut == NULL || pIn == NULL) { 81679 return; 81680 } 81681 for (i = 0; i < sampleCount; ++i) { 81682 *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; 81683 } 81684 } 81685 MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) 81686 { 81687 size_t i; 81688 if (pOut == NULL || pIn == NULL) { 81689 return; 81690 } 81691 for (i= 0; i < sampleCount; ++i) { 81692 *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; 81693 } 81694 } 81695 MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) 81696 { 81697 ma_uint64 sampleDataSize; 81698 ma_int16* pSampleData; 81699 ma_uint64 framesRead; 81700 MA_DR_WAV_ASSERT(pWav != NULL); 81701 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); 81702 if (sampleDataSize > MA_SIZE_MAX) { 81703 ma_dr_wav_uninit(pWav); 81704 return NULL; 81705 } 81706 pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); 81707 if (pSampleData == NULL) { 81708 ma_dr_wav_uninit(pWav); 81709 return NULL; 81710 } 81711 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); 81712 if (framesRead != pWav->totalPCMFrameCount) { 81713 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); 81714 ma_dr_wav_uninit(pWav); 81715 return NULL; 81716 } 81717 ma_dr_wav_uninit(pWav); 81718 if (sampleRate) { 81719 *sampleRate = pWav->sampleRate; 81720 } 81721 if (channels) { 81722 *channels = pWav->channels; 81723 } 81724 if (totalFrameCount) { 81725 *totalFrameCount = pWav->totalPCMFrameCount; 81726 } 81727 return pSampleData; 81728 } 81729 MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) 81730 { 81731 ma_uint64 sampleDataSize; 81732 float* pSampleData; 81733 ma_uint64 framesRead; 81734 MA_DR_WAV_ASSERT(pWav != NULL); 81735 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); 81736 if (sampleDataSize > MA_SIZE_MAX) { 81737 ma_dr_wav_uninit(pWav); 81738 return NULL; 81739 } 81740 pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); 81741 if (pSampleData == NULL) { 81742 ma_dr_wav_uninit(pWav); 81743 return NULL; 81744 } 81745 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); 81746 if (framesRead != pWav->totalPCMFrameCount) { 81747 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); 81748 ma_dr_wav_uninit(pWav); 81749 return NULL; 81750 } 81751 ma_dr_wav_uninit(pWav); 81752 if (sampleRate) { 81753 *sampleRate = pWav->sampleRate; 81754 } 81755 if (channels) { 81756 *channels = pWav->channels; 81757 } 81758 if (totalFrameCount) { 81759 *totalFrameCount = pWav->totalPCMFrameCount; 81760 } 81761 return pSampleData; 81762 } 81763 MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) 81764 { 81765 ma_uint64 sampleDataSize; 81766 ma_int32* pSampleData; 81767 ma_uint64 framesRead; 81768 MA_DR_WAV_ASSERT(pWav != NULL); 81769 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); 81770 if (sampleDataSize > MA_SIZE_MAX) { 81771 ma_dr_wav_uninit(pWav); 81772 return NULL; 81773 } 81774 pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); 81775 if (pSampleData == NULL) { 81776 ma_dr_wav_uninit(pWav); 81777 return NULL; 81778 } 81779 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); 81780 if (framesRead != pWav->totalPCMFrameCount) { 81781 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); 81782 ma_dr_wav_uninit(pWav); 81783 return NULL; 81784 } 81785 ma_dr_wav_uninit(pWav); 81786 if (sampleRate) { 81787 *sampleRate = pWav->sampleRate; 81788 } 81789 if (channels) { 81790 *channels = pWav->channels; 81791 } 81792 if (totalFrameCount) { 81793 *totalFrameCount = pWav->totalPCMFrameCount; 81794 } 81795 return pSampleData; 81796 } 81797 MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81798 { 81799 ma_dr_wav wav; 81800 if (channelsOut) { 81801 *channelsOut = 0; 81802 } 81803 if (sampleRateOut) { 81804 *sampleRateOut = 0; 81805 } 81806 if (totalFrameCountOut) { 81807 *totalFrameCountOut = 0; 81808 } 81809 if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { 81810 return NULL; 81811 } 81812 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81813 } 81814 MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81815 { 81816 ma_dr_wav wav; 81817 if (channelsOut) { 81818 *channelsOut = 0; 81819 } 81820 if (sampleRateOut) { 81821 *sampleRateOut = 0; 81822 } 81823 if (totalFrameCountOut) { 81824 *totalFrameCountOut = 0; 81825 } 81826 if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { 81827 return NULL; 81828 } 81829 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81830 } 81831 MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81832 { 81833 ma_dr_wav wav; 81834 if (channelsOut) { 81835 *channelsOut = 0; 81836 } 81837 if (sampleRateOut) { 81838 *sampleRateOut = 0; 81839 } 81840 if (totalFrameCountOut) { 81841 *totalFrameCountOut = 0; 81842 } 81843 if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { 81844 return NULL; 81845 } 81846 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81847 } 81848 #ifndef MA_DR_WAV_NO_STDIO 81849 MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81850 { 81851 ma_dr_wav wav; 81852 if (channelsOut) { 81853 *channelsOut = 0; 81854 } 81855 if (sampleRateOut) { 81856 *sampleRateOut = 0; 81857 } 81858 if (totalFrameCountOut) { 81859 *totalFrameCountOut = 0; 81860 } 81861 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { 81862 return NULL; 81863 } 81864 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81865 } 81866 MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81867 { 81868 ma_dr_wav wav; 81869 if (channelsOut) { 81870 *channelsOut = 0; 81871 } 81872 if (sampleRateOut) { 81873 *sampleRateOut = 0; 81874 } 81875 if (totalFrameCountOut) { 81876 *totalFrameCountOut = 0; 81877 } 81878 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { 81879 return NULL; 81880 } 81881 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81882 } 81883 MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81884 { 81885 ma_dr_wav wav; 81886 if (channelsOut) { 81887 *channelsOut = 0; 81888 } 81889 if (sampleRateOut) { 81890 *sampleRateOut = 0; 81891 } 81892 if (totalFrameCountOut) { 81893 *totalFrameCountOut = 0; 81894 } 81895 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { 81896 return NULL; 81897 } 81898 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81899 } 81900 #ifndef MA_DR_WAV_NO_WCHAR 81901 MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81902 { 81903 ma_dr_wav wav; 81904 if (sampleRateOut) { 81905 *sampleRateOut = 0; 81906 } 81907 if (channelsOut) { 81908 *channelsOut = 0; 81909 } 81910 if (totalFrameCountOut) { 81911 *totalFrameCountOut = 0; 81912 } 81913 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { 81914 return NULL; 81915 } 81916 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81917 } 81918 MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81919 { 81920 ma_dr_wav wav; 81921 if (sampleRateOut) { 81922 *sampleRateOut = 0; 81923 } 81924 if (channelsOut) { 81925 *channelsOut = 0; 81926 } 81927 if (totalFrameCountOut) { 81928 *totalFrameCountOut = 0; 81929 } 81930 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { 81931 return NULL; 81932 } 81933 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81934 } 81935 MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81936 { 81937 ma_dr_wav wav; 81938 if (sampleRateOut) { 81939 *sampleRateOut = 0; 81940 } 81941 if (channelsOut) { 81942 *channelsOut = 0; 81943 } 81944 if (totalFrameCountOut) { 81945 *totalFrameCountOut = 0; 81946 } 81947 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { 81948 return NULL; 81949 } 81950 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81951 } 81952 #endif 81953 #endif 81954 MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81955 { 81956 ma_dr_wav wav; 81957 if (channelsOut) { 81958 *channelsOut = 0; 81959 } 81960 if (sampleRateOut) { 81961 *sampleRateOut = 0; 81962 } 81963 if (totalFrameCountOut) { 81964 *totalFrameCountOut = 0; 81965 } 81966 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { 81967 return NULL; 81968 } 81969 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81970 } 81971 MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81972 { 81973 ma_dr_wav wav; 81974 if (channelsOut) { 81975 *channelsOut = 0; 81976 } 81977 if (sampleRateOut) { 81978 *sampleRateOut = 0; 81979 } 81980 if (totalFrameCountOut) { 81981 *totalFrameCountOut = 0; 81982 } 81983 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { 81984 return NULL; 81985 } 81986 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 81987 } 81988 MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 81989 { 81990 ma_dr_wav wav; 81991 if (channelsOut) { 81992 *channelsOut = 0; 81993 } 81994 if (sampleRateOut) { 81995 *sampleRateOut = 0; 81996 } 81997 if (totalFrameCountOut) { 81998 *totalFrameCountOut = 0; 81999 } 82000 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { 82001 return NULL; 82002 } 82003 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); 82004 } 82005 #endif 82006 MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 82007 { 82008 if (pAllocationCallbacks != NULL) { 82009 ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); 82010 } else { 82011 ma_dr_wav__free_default(p, NULL); 82012 } 82013 } 82014 MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) 82015 { 82016 return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); 82017 } 82018 MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) 82019 { 82020 return (ma_int16)ma_dr_wav_bytes_to_u16(data); 82021 } 82022 MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) 82023 { 82024 return ma_dr_wav_bytes_to_u32_le(data); 82025 } 82026 MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) 82027 { 82028 union { 82029 ma_uint32 u32; 82030 float f32; 82031 } value; 82032 value.u32 = ma_dr_wav_bytes_to_u32(data); 82033 return value.f32; 82034 } 82035 MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) 82036 { 82037 return (ma_int32)ma_dr_wav_bytes_to_u32(data); 82038 } 82039 MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) 82040 { 82041 return 82042 ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | 82043 ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); 82044 } 82045 MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) 82046 { 82047 return (ma_int64)ma_dr_wav_bytes_to_u64(data); 82048 } 82049 MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) 82050 { 82051 int i; 82052 for (i = 0; i < 16; i += 1) { 82053 if (a[i] != b[i]) { 82054 return MA_FALSE; 82055 } 82056 } 82057 return MA_TRUE; 82058 } 82059 MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) 82060 { 82061 return 82062 a[0] == b[0] && 82063 a[1] == b[1] && 82064 a[2] == b[2] && 82065 a[3] == b[3]; 82066 } 82067 #ifdef __MRC__ 82068 #pragma options opt reset 82069 #endif 82070 #endif 82071 /* dr_wav_c end */ 82072 #endif /* MA_DR_WAV_IMPLEMENTATION */ 82073 #endif /* MA_NO_WAV */ 82074 82075 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) 82076 #if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ 82077 /* dr_flac_c begin */ 82078 #ifndef ma_dr_flac_c 82079 #define ma_dr_flac_c 82080 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 82081 #pragma GCC diagnostic push 82082 #if __GNUC__ >= 7 82083 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 82084 #endif 82085 #endif 82086 #ifdef __linux__ 82087 #ifndef _BSD_SOURCE 82088 #define _BSD_SOURCE 82089 #endif 82090 #ifndef _DEFAULT_SOURCE 82091 #define _DEFAULT_SOURCE 82092 #endif 82093 #ifndef __USE_BSD 82094 #define __USE_BSD 82095 #endif 82096 #include <endian.h> 82097 #endif 82098 #include <stdlib.h> 82099 #include <string.h> 82100 #if !defined(MA_DR_FLAC_NO_SIMD) 82101 #if defined(MA_X64) || defined(MA_X86) 82102 #if defined(_MSC_VER) && !defined(__clang__) 82103 #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) 82104 #define MA_DR_FLAC_SUPPORT_SSE2 82105 #endif 82106 #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) 82107 #define MA_DR_FLAC_SUPPORT_SSE41 82108 #endif 82109 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) 82110 #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) 82111 #define MA_DR_FLAC_SUPPORT_SSE2 82112 #endif 82113 #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) 82114 #define MA_DR_FLAC_SUPPORT_SSE41 82115 #endif 82116 #endif 82117 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) 82118 #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include(<emmintrin.h>) 82119 #define MA_DR_FLAC_SUPPORT_SSE2 82120 #endif 82121 #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include(<smmintrin.h>) 82122 #define MA_DR_FLAC_SUPPORT_SSE41 82123 #endif 82124 #endif 82125 #if defined(MA_DR_FLAC_SUPPORT_SSE41) 82126 #include <smmintrin.h> 82127 #elif defined(MA_DR_FLAC_SUPPORT_SSE2) 82128 #include <emmintrin.h> 82129 #endif 82130 #endif 82131 #if defined(MA_ARM) 82132 #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) 82133 #define MA_DR_FLAC_SUPPORT_NEON 82134 #include <arm_neon.h> 82135 #endif 82136 #endif 82137 #endif 82138 #if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) 82139 #if defined(_MSC_VER) && !defined(__clang__) 82140 #if _MSC_VER >= 1400 82141 #include <intrin.h> 82142 static void ma_dr_flac__cpuid(int info[4], int fid) 82143 { 82144 __cpuid(info, fid); 82145 } 82146 #else 82147 #define MA_DR_FLAC_NO_CPUID 82148 #endif 82149 #else 82150 #if defined(__GNUC__) || defined(__clang__) 82151 static void ma_dr_flac__cpuid(int info[4], int fid) 82152 { 82153 #if defined(MA_X86) && defined(__PIC__) 82154 __asm__ __volatile__ ( 82155 "xchg{l} {%%}ebx, %k1;" 82156 "cpuid;" 82157 "xchg{l} {%%}ebx, %k1;" 82158 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) 82159 ); 82160 #else 82161 __asm__ __volatile__ ( 82162 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) 82163 ); 82164 #endif 82165 } 82166 #else 82167 #define MA_DR_FLAC_NO_CPUID 82168 #endif 82169 #endif 82170 #else 82171 #define MA_DR_FLAC_NO_CPUID 82172 #endif 82173 static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) 82174 { 82175 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 82176 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) 82177 #if defined(MA_X64) 82178 return MA_TRUE; 82179 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) 82180 return MA_TRUE; 82181 #else 82182 #if defined(MA_DR_FLAC_NO_CPUID) 82183 return MA_FALSE; 82184 #else 82185 int info[4]; 82186 ma_dr_flac__cpuid(info, 1); 82187 return (info[3] & (1 << 26)) != 0; 82188 #endif 82189 #endif 82190 #else 82191 return MA_FALSE; 82192 #endif 82193 #else 82194 return MA_FALSE; 82195 #endif 82196 } 82197 static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) 82198 { 82199 #if defined(MA_DR_FLAC_SUPPORT_SSE41) 82200 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) 82201 #if defined(__SSE4_1__) || defined(__AVX__) 82202 return MA_TRUE; 82203 #else 82204 #if defined(MA_DR_FLAC_NO_CPUID) 82205 return MA_FALSE; 82206 #else 82207 int info[4]; 82208 ma_dr_flac__cpuid(info, 1); 82209 return (info[2] & (1 << 19)) != 0; 82210 #endif 82211 #endif 82212 #else 82213 return MA_FALSE; 82214 #endif 82215 #else 82216 return MA_FALSE; 82217 #endif 82218 } 82219 #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) 82220 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC 82221 #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) 82222 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC 82223 #elif defined(__clang__) 82224 #if defined(__has_builtin) 82225 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) 82226 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC 82227 #endif 82228 #endif 82229 #endif 82230 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) 82231 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC 82232 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC 82233 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC 82234 #elif defined(__clang__) 82235 #if defined(__has_builtin) 82236 #if __has_builtin(__builtin_bswap16) 82237 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC 82238 #endif 82239 #if __has_builtin(__builtin_bswap32) 82240 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC 82241 #endif 82242 #if __has_builtin(__builtin_bswap64) 82243 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC 82244 #endif 82245 #endif 82246 #elif defined(__GNUC__) 82247 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) 82248 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC 82249 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC 82250 #endif 82251 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 82252 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC 82253 #endif 82254 #elif defined(__WATCOMC__) && defined(__386__) 82255 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC 82256 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC 82257 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC 82258 extern __inline ma_uint16 _watcom_bswap16(ma_uint16); 82259 extern __inline ma_uint32 _watcom_bswap32(ma_uint32); 82260 extern __inline ma_uint64 _watcom_bswap64(ma_uint64); 82261 #pragma aux _watcom_bswap16 = \ 82262 "xchg al, ah" \ 82263 parm [ax] \ 82264 value [ax] \ 82265 modify nomemory; 82266 #pragma aux _watcom_bswap32 = \ 82267 "bswap eax" \ 82268 parm [eax] \ 82269 value [eax] \ 82270 modify nomemory; 82271 #pragma aux _watcom_bswap64 = \ 82272 "bswap eax" \ 82273 "bswap edx" \ 82274 "xchg eax,edx" \ 82275 parm [eax edx] \ 82276 value [eax edx] \ 82277 modify nomemory; 82278 #endif 82279 #ifndef MA_DR_FLAC_ASSERT 82280 #include <assert.h> 82281 #define MA_DR_FLAC_ASSERT(expression) assert(expression) 82282 #endif 82283 #ifndef MA_DR_FLAC_MALLOC 82284 #define MA_DR_FLAC_MALLOC(sz) malloc((sz)) 82285 #endif 82286 #ifndef MA_DR_FLAC_REALLOC 82287 #define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) 82288 #endif 82289 #ifndef MA_DR_FLAC_FREE 82290 #define MA_DR_FLAC_FREE(p) free((p)) 82291 #endif 82292 #ifndef MA_DR_FLAC_COPY_MEMORY 82293 #define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) 82294 #endif 82295 #ifndef MA_DR_FLAC_ZERO_MEMORY 82296 #define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) 82297 #endif 82298 #ifndef MA_DR_FLAC_ZERO_OBJECT 82299 #define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) 82300 #endif 82301 #define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 82302 #define MA_DR_FLAC_SUBFRAME_CONSTANT 0 82303 #define MA_DR_FLAC_SUBFRAME_VERBATIM 1 82304 #define MA_DR_FLAC_SUBFRAME_FIXED 8 82305 #define MA_DR_FLAC_SUBFRAME_LPC 32 82306 #define MA_DR_FLAC_SUBFRAME_RESERVED 255 82307 #define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 82308 #define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 82309 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 82310 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 82311 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 82312 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 82313 #define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 82314 #define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 82315 #define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 82316 #define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) 82317 MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) 82318 { 82319 if (pMajor) { 82320 *pMajor = MA_DR_FLAC_VERSION_MAJOR; 82321 } 82322 if (pMinor) { 82323 *pMinor = MA_DR_FLAC_VERSION_MINOR; 82324 } 82325 if (pRevision) { 82326 *pRevision = MA_DR_FLAC_VERSION_REVISION; 82327 } 82328 } 82329 MA_API const char* ma_dr_flac_version_string(void) 82330 { 82331 return MA_DR_FLAC_VERSION_STRING; 82332 } 82333 #if defined(__has_feature) 82334 #if __has_feature(thread_sanitizer) 82335 #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) 82336 #else 82337 #define MA_DR_FLAC_NO_THREAD_SANITIZE 82338 #endif 82339 #else 82340 #define MA_DR_FLAC_NO_THREAD_SANITIZE 82341 #endif 82342 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) 82343 static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; 82344 #endif 82345 #ifndef MA_DR_FLAC_NO_CPUID 82346 static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; 82347 static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; 82348 MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) 82349 { 82350 static ma_bool32 isCPUCapsInitialized = MA_FALSE; 82351 if (!isCPUCapsInitialized) { 82352 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) 82353 int info[4] = {0}; 82354 ma_dr_flac__cpuid(info, 0x80000001); 82355 ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; 82356 #endif 82357 ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); 82358 ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); 82359 isCPUCapsInitialized = MA_TRUE; 82360 } 82361 } 82362 #else 82363 static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; 82364 static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) 82365 { 82366 #if defined(MA_DR_FLAC_SUPPORT_NEON) 82367 #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) 82368 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) 82369 return MA_TRUE; 82370 #else 82371 return MA_FALSE; 82372 #endif 82373 #else 82374 return MA_FALSE; 82375 #endif 82376 #else 82377 return MA_FALSE; 82378 #endif 82379 } 82380 MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) 82381 { 82382 ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); 82383 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) 82384 ma_dr_flac__gIsLZCNTSupported = MA_TRUE; 82385 #endif 82386 } 82387 #endif 82388 static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) 82389 { 82390 #if defined(MA_X86) || defined(MA_X64) 82391 return MA_TRUE; 82392 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN 82393 return MA_TRUE; 82394 #else 82395 int n = 1; 82396 return (*(char*)&n) == 1; 82397 #endif 82398 } 82399 static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) 82400 { 82401 #ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC 82402 #if defined(_MSC_VER) && !defined(__clang__) 82403 return _byteswap_ushort(n); 82404 #elif defined(__GNUC__) || defined(__clang__) 82405 return __builtin_bswap16(n); 82406 #elif defined(__WATCOMC__) && defined(__386__) 82407 return _watcom_bswap16(n); 82408 #else 82409 #error "This compiler does not support the byte swap intrinsic." 82410 #endif 82411 #else 82412 return ((n & 0xFF00) >> 8) | 82413 ((n & 0x00FF) << 8); 82414 #endif 82415 } 82416 static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) 82417 { 82418 #ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC 82419 #if defined(_MSC_VER) && !defined(__clang__) 82420 return _byteswap_ulong(n); 82421 #elif defined(__GNUC__) || defined(__clang__) 82422 #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) 82423 ma_uint32 r; 82424 __asm__ __volatile__ ( 82425 #if defined(MA_64BIT) 82426 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) 82427 #else 82428 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) 82429 #endif 82430 ); 82431 return r; 82432 #else 82433 return __builtin_bswap32(n); 82434 #endif 82435 #elif defined(__WATCOMC__) && defined(__386__) 82436 return _watcom_bswap32(n); 82437 #else 82438 #error "This compiler does not support the byte swap intrinsic." 82439 #endif 82440 #else 82441 return ((n & 0xFF000000) >> 24) | 82442 ((n & 0x00FF0000) >> 8) | 82443 ((n & 0x0000FF00) << 8) | 82444 ((n & 0x000000FF) << 24); 82445 #endif 82446 } 82447 static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) 82448 { 82449 #ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC 82450 #if defined(_MSC_VER) && !defined(__clang__) 82451 return _byteswap_uint64(n); 82452 #elif defined(__GNUC__) || defined(__clang__) 82453 return __builtin_bswap64(n); 82454 #elif defined(__WATCOMC__) && defined(__386__) 82455 return _watcom_bswap64(n); 82456 #else 82457 #error "This compiler does not support the byte swap intrinsic." 82458 #endif 82459 #else 82460 return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | 82461 ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | 82462 ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | 82463 ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | 82464 ((n & ((ma_uint64)0xFF000000 )) << 8) | 82465 ((n & ((ma_uint64)0x00FF0000 )) << 24) | 82466 ((n & ((ma_uint64)0x0000FF00 )) << 40) | 82467 ((n & ((ma_uint64)0x000000FF )) << 56); 82468 #endif 82469 } 82470 static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) 82471 { 82472 if (ma_dr_flac__is_little_endian()) { 82473 return ma_dr_flac__swap_endian_uint16(n); 82474 } 82475 return n; 82476 } 82477 static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) 82478 { 82479 if (ma_dr_flac__is_little_endian()) { 82480 return ma_dr_flac__swap_endian_uint32(n); 82481 } 82482 return n; 82483 } 82484 static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) 82485 { 82486 const ma_uint8* pNum = (ma_uint8*)pData; 82487 return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); 82488 } 82489 static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) 82490 { 82491 if (ma_dr_flac__is_little_endian()) { 82492 return ma_dr_flac__swap_endian_uint64(n); 82493 } 82494 return n; 82495 } 82496 static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) 82497 { 82498 if (!ma_dr_flac__is_little_endian()) { 82499 return ma_dr_flac__swap_endian_uint32(n); 82500 } 82501 return n; 82502 } 82503 static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) 82504 { 82505 const ma_uint8* pNum = (ma_uint8*)pData; 82506 return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; 82507 } 82508 static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) 82509 { 82510 ma_uint32 result = 0; 82511 result |= (n & 0x7F000000) >> 3; 82512 result |= (n & 0x007F0000) >> 2; 82513 result |= (n & 0x00007F00) >> 1; 82514 result |= (n & 0x0000007F) >> 0; 82515 return result; 82516 } 82517 static ma_uint8 ma_dr_flac__crc8_table[] = { 82518 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 82519 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 82520 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 82521 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 82522 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 82523 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 82524 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 82525 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 82526 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 82527 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 82528 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 82529 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 82530 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 82531 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 82532 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 82533 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 82534 }; 82535 static ma_uint16 ma_dr_flac__crc16_table[] = { 82536 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 82537 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 82538 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 82539 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, 82540 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 82541 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, 82542 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 82543 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, 82544 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 82545 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, 82546 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 82547 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, 82548 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 82549 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, 82550 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 82551 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, 82552 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 82553 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, 82554 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 82555 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, 82556 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 82557 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, 82558 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 82559 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, 82560 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 82561 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, 82562 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 82563 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, 82564 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 82565 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, 82566 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 82567 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 82568 }; 82569 static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) 82570 { 82571 return ma_dr_flac__crc8_table[crc ^ data]; 82572 } 82573 static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) 82574 { 82575 #ifdef MA_DR_FLAC_NO_CRC 82576 (void)crc; 82577 (void)data; 82578 (void)count; 82579 return 0; 82580 #else 82581 #if 0 82582 ma_uint8 p = 0x07; 82583 for (int i = count-1; i >= 0; --i) { 82584 ma_uint8 bit = (data & (1 << i)) >> i; 82585 if (crc & 0x80) { 82586 crc = ((crc << 1) | bit) ^ p; 82587 } else { 82588 crc = ((crc << 1) | bit); 82589 } 82590 } 82591 return crc; 82592 #else 82593 ma_uint32 wholeBytes; 82594 ma_uint32 leftoverBits; 82595 ma_uint64 leftoverDataMask; 82596 static ma_uint64 leftoverDataMaskTable[8] = { 82597 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F 82598 }; 82599 MA_DR_FLAC_ASSERT(count <= 32); 82600 wholeBytes = count >> 3; 82601 leftoverBits = count - (wholeBytes*8); 82602 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; 82603 switch (wholeBytes) { 82604 case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); 82605 case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); 82606 case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); 82607 case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); 82608 case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); 82609 } 82610 return crc; 82611 #endif 82612 #endif 82613 } 82614 static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) 82615 { 82616 return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; 82617 } 82618 static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) 82619 { 82620 #ifdef MA_64BIT 82621 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); 82622 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); 82623 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); 82624 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); 82625 #endif 82626 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); 82627 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); 82628 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); 82629 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); 82630 return crc; 82631 } 82632 static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) 82633 { 82634 switch (byteCount) 82635 { 82636 #ifdef MA_64BIT 82637 case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); 82638 case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); 82639 case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); 82640 case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); 82641 #endif 82642 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); 82643 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); 82644 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); 82645 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); 82646 } 82647 return crc; 82648 } 82649 #if 0 82650 static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) 82651 { 82652 #ifdef MA_DR_FLAC_NO_CRC 82653 (void)crc; 82654 (void)data; 82655 (void)count; 82656 return 0; 82657 #else 82658 #if 0 82659 ma_uint16 p = 0x8005; 82660 for (int i = count-1; i >= 0; --i) { 82661 ma_uint16 bit = (data & (1ULL << i)) >> i; 82662 if (r & 0x8000) { 82663 r = ((r << 1) | bit) ^ p; 82664 } else { 82665 r = ((r << 1) | bit); 82666 } 82667 } 82668 return crc; 82669 #else 82670 ma_uint32 wholeBytes; 82671 ma_uint32 leftoverBits; 82672 ma_uint64 leftoverDataMask; 82673 static ma_uint64 leftoverDataMaskTable[8] = { 82674 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F 82675 }; 82676 MA_DR_FLAC_ASSERT(count <= 64); 82677 wholeBytes = count >> 3; 82678 leftoverBits = count & 7; 82679 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; 82680 switch (wholeBytes) { 82681 default: 82682 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); 82683 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); 82684 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); 82685 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); 82686 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; 82687 } 82688 return crc; 82689 #endif 82690 #endif 82691 } 82692 static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) 82693 { 82694 #ifdef MA_DR_FLAC_NO_CRC 82695 (void)crc; 82696 (void)data; 82697 (void)count; 82698 return 0; 82699 #else 82700 ma_uint32 wholeBytes; 82701 ma_uint32 leftoverBits; 82702 ma_uint64 leftoverDataMask; 82703 static ma_uint64 leftoverDataMaskTable[8] = { 82704 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F 82705 }; 82706 MA_DR_FLAC_ASSERT(count <= 64); 82707 wholeBytes = count >> 3; 82708 leftoverBits = count & 7; 82709 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; 82710 switch (wholeBytes) { 82711 default: 82712 case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); 82713 case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); 82714 case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); 82715 case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); 82716 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); 82717 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); 82718 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); 82719 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); 82720 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; 82721 } 82722 return crc; 82723 #endif 82724 } 82725 static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) 82726 { 82727 #ifdef MA_64BIT 82728 return ma_dr_flac_crc16__64bit(crc, data, count); 82729 #else 82730 return ma_dr_flac_crc16__32bit(crc, data, count); 82731 #endif 82732 } 82733 #endif 82734 #ifdef MA_64BIT 82735 #define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 82736 #else 82737 #define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 82738 #endif 82739 #define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) 82740 #define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) 82741 #define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) 82742 #define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) 82743 #define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) 82744 #define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) 82745 #define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) 82746 #define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) 82747 #define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) 82748 #define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) 82749 #define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) 82750 #ifndef MA_DR_FLAC_NO_CRC 82751 static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) 82752 { 82753 bs->crc16 = 0; 82754 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; 82755 } 82756 static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) 82757 { 82758 if (bs->crc16CacheIgnoredBytes == 0) { 82759 bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); 82760 } else { 82761 bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); 82762 bs->crc16CacheIgnoredBytes = 0; 82763 } 82764 } 82765 static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) 82766 { 82767 MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); 82768 if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { 82769 ma_dr_flac__update_crc16(bs); 82770 } else { 82771 bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); 82772 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; 82773 } 82774 return bs->crc16; 82775 } 82776 #endif 82777 static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) 82778 { 82779 size_t bytesRead; 82780 size_t alignedL1LineCount; 82781 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 82782 bs->cache = bs->cacheL2[bs->nextL2Line++]; 82783 return MA_TRUE; 82784 } 82785 if (bs->unalignedByteCount > 0) { 82786 return MA_FALSE; 82787 } 82788 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); 82789 bs->nextL2Line = 0; 82790 if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { 82791 bs->cache = bs->cacheL2[bs->nextL2Line++]; 82792 return MA_TRUE; 82793 } 82794 alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); 82795 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); 82796 if (bs->unalignedByteCount > 0) { 82797 bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; 82798 } 82799 if (alignedL1LineCount > 0) { 82800 size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; 82801 size_t i; 82802 for (i = alignedL1LineCount; i > 0; --i) { 82803 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; 82804 } 82805 bs->nextL2Line = (ma_uint32)offset; 82806 bs->cache = bs->cacheL2[bs->nextL2Line++]; 82807 return MA_TRUE; 82808 } else { 82809 bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); 82810 return MA_FALSE; 82811 } 82812 } 82813 static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) 82814 { 82815 size_t bytesRead; 82816 #ifndef MA_DR_FLAC_NO_CRC 82817 ma_dr_flac__update_crc16(bs); 82818 #endif 82819 if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { 82820 bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); 82821 bs->consumedBits = 0; 82822 #ifndef MA_DR_FLAC_NO_CRC 82823 bs->crc16Cache = bs->cache; 82824 #endif 82825 return MA_TRUE; 82826 } 82827 bytesRead = bs->unalignedByteCount; 82828 if (bytesRead == 0) { 82829 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 82830 return MA_FALSE; 82831 } 82832 MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); 82833 bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; 82834 bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); 82835 bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); 82836 bs->unalignedByteCount = 0; 82837 #ifndef MA_DR_FLAC_NO_CRC 82838 bs->crc16Cache = bs->cache >> bs->consumedBits; 82839 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; 82840 #endif 82841 return MA_TRUE; 82842 } 82843 static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) 82844 { 82845 bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); 82846 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 82847 bs->cache = 0; 82848 bs->unalignedByteCount = 0; 82849 bs->unalignedCache = 0; 82850 #ifndef MA_DR_FLAC_NO_CRC 82851 bs->crc16Cache = 0; 82852 bs->crc16CacheIgnoredBytes = 0; 82853 #endif 82854 } 82855 static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) 82856 { 82857 MA_DR_FLAC_ASSERT(bs != NULL); 82858 MA_DR_FLAC_ASSERT(pResultOut != NULL); 82859 MA_DR_FLAC_ASSERT(bitCount > 0); 82860 MA_DR_FLAC_ASSERT(bitCount <= 32); 82861 if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { 82862 if (!ma_dr_flac__reload_cache(bs)) { 82863 return MA_FALSE; 82864 } 82865 } 82866 if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 82867 #ifdef MA_64BIT 82868 *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); 82869 bs->consumedBits += bitCount; 82870 bs->cache <<= bitCount; 82871 #else 82872 if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { 82873 *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); 82874 bs->consumedBits += bitCount; 82875 bs->cache <<= bitCount; 82876 } else { 82877 *pResultOut = (ma_uint32)bs->cache; 82878 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 82879 bs->cache = 0; 82880 } 82881 #endif 82882 return MA_TRUE; 82883 } else { 82884 ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); 82885 ma_uint32 bitCountLo = bitCount - bitCountHi; 82886 ma_uint32 resultHi; 82887 MA_DR_FLAC_ASSERT(bitCountHi > 0); 82888 MA_DR_FLAC_ASSERT(bitCountHi < 32); 82889 resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); 82890 if (!ma_dr_flac__reload_cache(bs)) { 82891 return MA_FALSE; 82892 } 82893 if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 82894 return MA_FALSE; 82895 } 82896 *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); 82897 bs->consumedBits += bitCountLo; 82898 bs->cache <<= bitCountLo; 82899 return MA_TRUE; 82900 } 82901 } 82902 static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) 82903 { 82904 ma_uint32 result; 82905 MA_DR_FLAC_ASSERT(bs != NULL); 82906 MA_DR_FLAC_ASSERT(pResult != NULL); 82907 MA_DR_FLAC_ASSERT(bitCount > 0); 82908 MA_DR_FLAC_ASSERT(bitCount <= 32); 82909 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { 82910 return MA_FALSE; 82911 } 82912 if (bitCount < 32) { 82913 ma_uint32 signbit; 82914 signbit = ((result >> (bitCount-1)) & 0x01); 82915 result |= (~signbit + 1) << bitCount; 82916 } 82917 *pResult = (ma_int32)result; 82918 return MA_TRUE; 82919 } 82920 #ifdef MA_64BIT 82921 static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) 82922 { 82923 ma_uint32 resultHi; 82924 ma_uint32 resultLo; 82925 MA_DR_FLAC_ASSERT(bitCount <= 64); 82926 MA_DR_FLAC_ASSERT(bitCount > 32); 82927 if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { 82928 return MA_FALSE; 82929 } 82930 if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { 82931 return MA_FALSE; 82932 } 82933 *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); 82934 return MA_TRUE; 82935 } 82936 #endif 82937 #if 0 82938 static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) 82939 { 82940 ma_uint64 result; 82941 ma_uint64 signbit; 82942 MA_DR_FLAC_ASSERT(bitCount <= 64); 82943 if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { 82944 return MA_FALSE; 82945 } 82946 signbit = ((result >> (bitCount-1)) & 0x01); 82947 result |= (~signbit + 1) << bitCount; 82948 *pResultOut = (ma_int64)result; 82949 return MA_TRUE; 82950 } 82951 #endif 82952 static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) 82953 { 82954 ma_uint32 result; 82955 MA_DR_FLAC_ASSERT(bs != NULL); 82956 MA_DR_FLAC_ASSERT(pResult != NULL); 82957 MA_DR_FLAC_ASSERT(bitCount > 0); 82958 MA_DR_FLAC_ASSERT(bitCount <= 16); 82959 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { 82960 return MA_FALSE; 82961 } 82962 *pResult = (ma_uint16)result; 82963 return MA_TRUE; 82964 } 82965 #if 0 82966 static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) 82967 { 82968 ma_int32 result; 82969 MA_DR_FLAC_ASSERT(bs != NULL); 82970 MA_DR_FLAC_ASSERT(pResult != NULL); 82971 MA_DR_FLAC_ASSERT(bitCount > 0); 82972 MA_DR_FLAC_ASSERT(bitCount <= 16); 82973 if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { 82974 return MA_FALSE; 82975 } 82976 *pResult = (ma_int16)result; 82977 return MA_TRUE; 82978 } 82979 #endif 82980 static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) 82981 { 82982 ma_uint32 result; 82983 MA_DR_FLAC_ASSERT(bs != NULL); 82984 MA_DR_FLAC_ASSERT(pResult != NULL); 82985 MA_DR_FLAC_ASSERT(bitCount > 0); 82986 MA_DR_FLAC_ASSERT(bitCount <= 8); 82987 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { 82988 return MA_FALSE; 82989 } 82990 *pResult = (ma_uint8)result; 82991 return MA_TRUE; 82992 } 82993 static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) 82994 { 82995 ma_int32 result; 82996 MA_DR_FLAC_ASSERT(bs != NULL); 82997 MA_DR_FLAC_ASSERT(pResult != NULL); 82998 MA_DR_FLAC_ASSERT(bitCount > 0); 82999 MA_DR_FLAC_ASSERT(bitCount <= 8); 83000 if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { 83001 return MA_FALSE; 83002 } 83003 *pResult = (ma_int8)result; 83004 return MA_TRUE; 83005 } 83006 static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) 83007 { 83008 if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83009 bs->consumedBits += (ma_uint32)bitsToSeek; 83010 bs->cache <<= bitsToSeek; 83011 return MA_TRUE; 83012 } else { 83013 bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); 83014 bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); 83015 bs->cache = 0; 83016 #ifdef MA_64BIT 83017 while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { 83018 ma_uint64 bin; 83019 if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { 83020 return MA_FALSE; 83021 } 83022 bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 83023 } 83024 #else 83025 while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { 83026 ma_uint32 bin; 83027 if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { 83028 return MA_FALSE; 83029 } 83030 bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 83031 } 83032 #endif 83033 while (bitsToSeek >= 8) { 83034 ma_uint8 bin; 83035 if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { 83036 return MA_FALSE; 83037 } 83038 bitsToSeek -= 8; 83039 } 83040 if (bitsToSeek > 0) { 83041 ma_uint8 bin; 83042 if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { 83043 return MA_FALSE; 83044 } 83045 bitsToSeek = 0; 83046 } 83047 MA_DR_FLAC_ASSERT(bitsToSeek == 0); 83048 return MA_TRUE; 83049 } 83050 } 83051 static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) 83052 { 83053 MA_DR_FLAC_ASSERT(bs != NULL); 83054 if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { 83055 return MA_FALSE; 83056 } 83057 for (;;) { 83058 ma_uint8 hi; 83059 #ifndef MA_DR_FLAC_NO_CRC 83060 ma_dr_flac__reset_crc16(bs); 83061 #endif 83062 if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { 83063 return MA_FALSE; 83064 } 83065 if (hi == 0xFF) { 83066 ma_uint8 lo; 83067 if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { 83068 return MA_FALSE; 83069 } 83070 if (lo == 0x3E) { 83071 return MA_TRUE; 83072 } else { 83073 if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { 83074 return MA_FALSE; 83075 } 83076 } 83077 } 83078 } 83079 } 83080 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) 83081 #define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT 83082 #endif 83083 #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) 83084 #define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC 83085 #endif 83086 #if defined(__WATCOMC__) && defined(__386__) 83087 #define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM 83088 #endif 83089 #ifdef __MRC__ 83090 #include <intrinsics.h> 83091 #define MA_DR_FLAC_IMPLEMENT_CLZ_MRC 83092 #endif 83093 static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) 83094 { 83095 ma_uint32 n; 83096 static ma_uint32 clz_table_4[] = { 83097 0, 83098 4, 83099 3, 3, 83100 2, 2, 2, 2, 83101 1, 1, 1, 1, 1, 1, 1, 1 83102 }; 83103 if (x == 0) { 83104 return sizeof(x)*8; 83105 } 83106 n = clz_table_4[x >> (sizeof(x)*8 - 4)]; 83107 if (n == 0) { 83108 #ifdef MA_64BIT 83109 if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } 83110 if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } 83111 if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } 83112 if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } 83113 #else 83114 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } 83115 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } 83116 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } 83117 #endif 83118 n += clz_table_4[x >> (sizeof(x)*8 - 4)]; 83119 } 83120 return n - 1; 83121 } 83122 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT 83123 static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) 83124 { 83125 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) 83126 return MA_TRUE; 83127 #elif defined(__MRC__) 83128 return MA_TRUE; 83129 #else 83130 #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC 83131 return ma_dr_flac__gIsLZCNTSupported; 83132 #else 83133 return MA_FALSE; 83134 #endif 83135 #endif 83136 } 83137 static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) 83138 { 83139 #if defined(_MSC_VER) 83140 #ifdef MA_64BIT 83141 return (ma_uint32)__lzcnt64(x); 83142 #else 83143 return (ma_uint32)__lzcnt(x); 83144 #endif 83145 #else 83146 #if defined(__GNUC__) || defined(__clang__) 83147 #if defined(MA_X64) 83148 { 83149 ma_uint64 r; 83150 __asm__ __volatile__ ( 83151 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" 83152 ); 83153 return (ma_uint32)r; 83154 } 83155 #elif defined(MA_X86) 83156 { 83157 ma_uint32 r; 83158 __asm__ __volatile__ ( 83159 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" 83160 ); 83161 return r; 83162 } 83163 #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) 83164 { 83165 unsigned int r; 83166 __asm__ __volatile__ ( 83167 #if defined(MA_64BIT) 83168 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) 83169 #else 83170 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) 83171 #endif 83172 ); 83173 return r; 83174 } 83175 #else 83176 if (x == 0) { 83177 return sizeof(x)*8; 83178 } 83179 #ifdef MA_64BIT 83180 return (ma_uint32)__builtin_clzll((ma_uint64)x); 83181 #else 83182 return (ma_uint32)__builtin_clzl((ma_uint32)x); 83183 #endif 83184 #endif 83185 #else 83186 #error "This compiler does not support the lzcnt intrinsic." 83187 #endif 83188 #endif 83189 } 83190 #endif 83191 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC 83192 #include <intrin.h> 83193 static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) 83194 { 83195 ma_uint32 n; 83196 if (x == 0) { 83197 return sizeof(x)*8; 83198 } 83199 #ifdef MA_64BIT 83200 _BitScanReverse64((unsigned long*)&n, x); 83201 #else 83202 _BitScanReverse((unsigned long*)&n, x); 83203 #endif 83204 return sizeof(x)*8 - n - 1; 83205 } 83206 #endif 83207 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM 83208 static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); 83209 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT 83210 #pragma aux ma_dr_flac__clz_watcom_lzcnt = \ 83211 "db 0F3h, 0Fh, 0BDh, 0C0h" \ 83212 parm [eax] \ 83213 value [eax] \ 83214 modify nomemory; 83215 #else 83216 #pragma aux ma_dr_flac__clz_watcom = \ 83217 "bsr eax, eax" \ 83218 "xor eax, 31" \ 83219 parm [eax] nomemory \ 83220 value [eax] \ 83221 modify exact [eax] nomemory; 83222 #endif 83223 #endif 83224 static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) 83225 { 83226 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT 83227 if (ma_dr_flac__is_lzcnt_supported()) { 83228 return ma_dr_flac__clz_lzcnt(x); 83229 } else 83230 #endif 83231 { 83232 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC 83233 return ma_dr_flac__clz_msvc(x); 83234 #elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) 83235 return ma_dr_flac__clz_watcom_lzcnt(x); 83236 #elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) 83237 return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); 83238 #elif defined(__MRC__) 83239 return __cntlzw(x); 83240 #else 83241 return ma_dr_flac__clz_software(x); 83242 #endif 83243 } 83244 } 83245 static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) 83246 { 83247 ma_uint32 zeroCounter = 0; 83248 ma_uint32 setBitOffsetPlus1; 83249 while (bs->cache == 0) { 83250 zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); 83251 if (!ma_dr_flac__reload_cache(bs)) { 83252 return MA_FALSE; 83253 } 83254 } 83255 if (bs->cache == 1) { 83256 *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; 83257 if (!ma_dr_flac__reload_cache(bs)) { 83258 return MA_FALSE; 83259 } 83260 return MA_TRUE; 83261 } 83262 setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); 83263 setBitOffsetPlus1 += 1; 83264 if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83265 return MA_FALSE; 83266 } 83267 bs->consumedBits += setBitOffsetPlus1; 83268 bs->cache <<= setBitOffsetPlus1; 83269 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; 83270 return MA_TRUE; 83271 } 83272 static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) 83273 { 83274 MA_DR_FLAC_ASSERT(bs != NULL); 83275 MA_DR_FLAC_ASSERT(offsetFromStart > 0); 83276 if (offsetFromStart > 0x7FFFFFFF) { 83277 ma_uint64 bytesRemaining = offsetFromStart; 83278 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { 83279 return MA_FALSE; 83280 } 83281 bytesRemaining -= 0x7FFFFFFF; 83282 while (bytesRemaining > 0x7FFFFFFF) { 83283 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { 83284 return MA_FALSE; 83285 } 83286 bytesRemaining -= 0x7FFFFFFF; 83287 } 83288 if (bytesRemaining > 0) { 83289 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { 83290 return MA_FALSE; 83291 } 83292 } 83293 } else { 83294 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { 83295 return MA_FALSE; 83296 } 83297 } 83298 ma_dr_flac__reset_cache(bs); 83299 return MA_TRUE; 83300 } 83301 static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) 83302 { 83303 ma_uint8 crc; 83304 ma_uint64 result; 83305 ma_uint8 utf8[7] = {0}; 83306 int byteCount; 83307 int i; 83308 MA_DR_FLAC_ASSERT(bs != NULL); 83309 MA_DR_FLAC_ASSERT(pNumberOut != NULL); 83310 MA_DR_FLAC_ASSERT(pCRCOut != NULL); 83311 crc = *pCRCOut; 83312 if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { 83313 *pNumberOut = 0; 83314 return MA_AT_END; 83315 } 83316 crc = ma_dr_flac_crc8(crc, utf8[0], 8); 83317 if ((utf8[0] & 0x80) == 0) { 83318 *pNumberOut = utf8[0]; 83319 *pCRCOut = crc; 83320 return MA_SUCCESS; 83321 } 83322 if ((utf8[0] & 0xE0) == 0xC0) { 83323 byteCount = 2; 83324 } else if ((utf8[0] & 0xF0) == 0xE0) { 83325 byteCount = 3; 83326 } else if ((utf8[0] & 0xF8) == 0xF0) { 83327 byteCount = 4; 83328 } else if ((utf8[0] & 0xFC) == 0xF8) { 83329 byteCount = 5; 83330 } else if ((utf8[0] & 0xFE) == 0xFC) { 83331 byteCount = 6; 83332 } else if ((utf8[0] & 0xFF) == 0xFE) { 83333 byteCount = 7; 83334 } else { 83335 *pNumberOut = 0; 83336 return MA_CRC_MISMATCH; 83337 } 83338 MA_DR_FLAC_ASSERT(byteCount > 1); 83339 result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); 83340 for (i = 1; i < byteCount; ++i) { 83341 if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { 83342 *pNumberOut = 0; 83343 return MA_AT_END; 83344 } 83345 crc = ma_dr_flac_crc8(crc, utf8[i], 8); 83346 result = (result << 6) | (utf8[i] & 0x3F); 83347 } 83348 *pNumberOut = result; 83349 *pCRCOut = crc; 83350 return MA_SUCCESS; 83351 } 83352 static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) 83353 { 83354 #if 1 83355 ma_uint32 result = 0; 83356 while (x > 0) { 83357 result += 1; 83358 x >>= 1; 83359 } 83360 return result; 83361 #endif 83362 } 83363 static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) 83364 { 83365 return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; 83366 } 83367 #if defined(__clang__) 83368 __attribute__((no_sanitize("signed-integer-overflow"))) 83369 #endif 83370 static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) 83371 { 83372 ma_int32 prediction = 0; 83373 MA_DR_FLAC_ASSERT(order <= 32); 83374 switch (order) 83375 { 83376 case 32: prediction += coefficients[31] * pDecodedSamples[-32]; 83377 case 31: prediction += coefficients[30] * pDecodedSamples[-31]; 83378 case 30: prediction += coefficients[29] * pDecodedSamples[-30]; 83379 case 29: prediction += coefficients[28] * pDecodedSamples[-29]; 83380 case 28: prediction += coefficients[27] * pDecodedSamples[-28]; 83381 case 27: prediction += coefficients[26] * pDecodedSamples[-27]; 83382 case 26: prediction += coefficients[25] * pDecodedSamples[-26]; 83383 case 25: prediction += coefficients[24] * pDecodedSamples[-25]; 83384 case 24: prediction += coefficients[23] * pDecodedSamples[-24]; 83385 case 23: prediction += coefficients[22] * pDecodedSamples[-23]; 83386 case 22: prediction += coefficients[21] * pDecodedSamples[-22]; 83387 case 21: prediction += coefficients[20] * pDecodedSamples[-21]; 83388 case 20: prediction += coefficients[19] * pDecodedSamples[-20]; 83389 case 19: prediction += coefficients[18] * pDecodedSamples[-19]; 83390 case 18: prediction += coefficients[17] * pDecodedSamples[-18]; 83391 case 17: prediction += coefficients[16] * pDecodedSamples[-17]; 83392 case 16: prediction += coefficients[15] * pDecodedSamples[-16]; 83393 case 15: prediction += coefficients[14] * pDecodedSamples[-15]; 83394 case 14: prediction += coefficients[13] * pDecodedSamples[-14]; 83395 case 13: prediction += coefficients[12] * pDecodedSamples[-13]; 83396 case 12: prediction += coefficients[11] * pDecodedSamples[-12]; 83397 case 11: prediction += coefficients[10] * pDecodedSamples[-11]; 83398 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; 83399 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; 83400 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; 83401 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; 83402 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; 83403 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; 83404 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; 83405 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; 83406 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; 83407 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; 83408 } 83409 return (ma_int32)(prediction >> shift); 83410 } 83411 static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) 83412 { 83413 ma_int64 prediction; 83414 MA_DR_FLAC_ASSERT(order <= 32); 83415 #ifndef MA_64BIT 83416 if (order == 8) 83417 { 83418 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83419 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83420 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83421 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83422 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83423 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83424 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83425 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; 83426 } 83427 else if (order == 7) 83428 { 83429 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83430 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83431 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83432 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83433 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83434 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83435 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83436 } 83437 else if (order == 3) 83438 { 83439 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83440 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83441 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83442 } 83443 else if (order == 6) 83444 { 83445 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83446 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83447 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83448 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83449 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83450 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83451 } 83452 else if (order == 5) 83453 { 83454 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83455 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83456 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83457 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83458 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83459 } 83460 else if (order == 4) 83461 { 83462 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83463 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83464 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83465 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83466 } 83467 else if (order == 12) 83468 { 83469 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83470 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83471 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83472 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83473 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83474 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83475 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83476 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; 83477 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; 83478 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; 83479 prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; 83480 prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; 83481 } 83482 else if (order == 2) 83483 { 83484 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83485 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83486 } 83487 else if (order == 1) 83488 { 83489 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83490 } 83491 else if (order == 10) 83492 { 83493 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83494 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83495 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83496 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83497 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83498 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83499 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83500 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; 83501 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; 83502 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; 83503 } 83504 else if (order == 9) 83505 { 83506 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83507 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83508 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83509 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83510 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83511 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83512 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83513 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; 83514 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; 83515 } 83516 else if (order == 11) 83517 { 83518 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; 83519 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; 83520 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; 83521 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; 83522 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; 83523 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; 83524 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; 83525 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; 83526 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; 83527 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; 83528 prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; 83529 } 83530 else 83531 { 83532 int j; 83533 prediction = 0; 83534 for (j = 0; j < (int)order; ++j) { 83535 prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; 83536 } 83537 } 83538 #endif 83539 #ifdef MA_64BIT 83540 prediction = 0; 83541 switch (order) 83542 { 83543 case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; 83544 case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; 83545 case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; 83546 case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; 83547 case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; 83548 case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; 83549 case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; 83550 case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; 83551 case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; 83552 case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; 83553 case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; 83554 case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; 83555 case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; 83556 case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; 83557 case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; 83558 case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; 83559 case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; 83560 case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; 83561 case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; 83562 case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; 83563 case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; 83564 case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; 83565 case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; 83566 case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; 83567 case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; 83568 case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; 83569 case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; 83570 case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; 83571 case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; 83572 case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; 83573 case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; 83574 case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; 83575 } 83576 #endif 83577 return (ma_int32)(prediction >> shift); 83578 } 83579 #if 0 83580 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 83581 { 83582 ma_uint32 i; 83583 MA_DR_FLAC_ASSERT(bs != NULL); 83584 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 83585 for (i = 0; i < count; ++i) { 83586 ma_uint32 zeroCounter = 0; 83587 for (;;) { 83588 ma_uint8 bit; 83589 if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { 83590 return MA_FALSE; 83591 } 83592 if (bit == 0) { 83593 zeroCounter += 1; 83594 } else { 83595 break; 83596 } 83597 } 83598 ma_uint32 decodedRice; 83599 if (riceParam > 0) { 83600 if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { 83601 return MA_FALSE; 83602 } 83603 } else { 83604 decodedRice = 0; 83605 } 83606 decodedRice |= (zeroCounter << riceParam); 83607 if ((decodedRice & 0x01)) { 83608 decodedRice = ~(decodedRice >> 1); 83609 } else { 83610 decodedRice = (decodedRice >> 1); 83611 } 83612 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 83613 pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); 83614 } else { 83615 pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); 83616 } 83617 } 83618 return MA_TRUE; 83619 } 83620 #endif 83621 #if 0 83622 static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) 83623 { 83624 ma_uint32 zeroCounter = 0; 83625 ma_uint32 decodedRice; 83626 for (;;) { 83627 ma_uint8 bit; 83628 if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { 83629 return MA_FALSE; 83630 } 83631 if (bit == 0) { 83632 zeroCounter += 1; 83633 } else { 83634 break; 83635 } 83636 } 83637 if (riceParam > 0) { 83638 if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { 83639 return MA_FALSE; 83640 } 83641 } else { 83642 decodedRice = 0; 83643 } 83644 *pZeroCounterOut = zeroCounter; 83645 *pRiceParamPartOut = decodedRice; 83646 return MA_TRUE; 83647 } 83648 #endif 83649 #if 0 83650 static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) 83651 { 83652 ma_dr_flac_cache_t riceParamMask; 83653 ma_uint32 zeroCounter; 83654 ma_uint32 setBitOffsetPlus1; 83655 ma_uint32 riceParamPart; 83656 ma_uint32 riceLength; 83657 MA_DR_FLAC_ASSERT(riceParam > 0); 83658 riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); 83659 zeroCounter = 0; 83660 while (bs->cache == 0) { 83661 zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); 83662 if (!ma_dr_flac__reload_cache(bs)) { 83663 return MA_FALSE; 83664 } 83665 } 83666 setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); 83667 zeroCounter += setBitOffsetPlus1; 83668 setBitOffsetPlus1 += 1; 83669 riceLength = setBitOffsetPlus1 + riceParam; 83670 if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83671 riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); 83672 bs->consumedBits += riceLength; 83673 bs->cache <<= riceLength; 83674 } else { 83675 ma_uint32 bitCountLo; 83676 ma_dr_flac_cache_t resultHi; 83677 bs->consumedBits += riceLength; 83678 bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); 83679 bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); 83680 resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); 83681 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 83682 #ifndef MA_DR_FLAC_NO_CRC 83683 ma_dr_flac__update_crc16(bs); 83684 #endif 83685 bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); 83686 bs->consumedBits = 0; 83687 #ifndef MA_DR_FLAC_NO_CRC 83688 bs->crc16Cache = bs->cache; 83689 #endif 83690 } else { 83691 if (!ma_dr_flac__reload_cache(bs)) { 83692 return MA_FALSE; 83693 } 83694 if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83695 return MA_FALSE; 83696 } 83697 } 83698 riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); 83699 bs->consumedBits += bitCountLo; 83700 bs->cache <<= bitCountLo; 83701 } 83702 pZeroCounterOut[0] = zeroCounter; 83703 pRiceParamPartOut[0] = riceParamPart; 83704 return MA_TRUE; 83705 } 83706 #endif 83707 static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) 83708 { 83709 ma_uint32 riceParamPlus1 = riceParam + 1; 83710 ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); 83711 ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; 83712 ma_dr_flac_cache_t bs_cache = bs->cache; 83713 ma_uint32 bs_consumedBits = bs->consumedBits; 83714 ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); 83715 if (lzcount < sizeof(bs_cache)*8) { 83716 pZeroCounterOut[0] = lzcount; 83717 extract_rice_param_part: 83718 bs_cache <<= lzcount; 83719 bs_consumedBits += lzcount; 83720 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { 83721 pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); 83722 bs_cache <<= riceParamPlus1; 83723 bs_consumedBits += riceParamPlus1; 83724 } else { 83725 ma_uint32 riceParamPartHi; 83726 ma_uint32 riceParamPartLo; 83727 ma_uint32 riceParamPartLoBitCount; 83728 riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); 83729 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; 83730 MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); 83731 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 83732 #ifndef MA_DR_FLAC_NO_CRC 83733 ma_dr_flac__update_crc16(bs); 83734 #endif 83735 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); 83736 bs_consumedBits = riceParamPartLoBitCount; 83737 #ifndef MA_DR_FLAC_NO_CRC 83738 bs->crc16Cache = bs_cache; 83739 #endif 83740 } else { 83741 if (!ma_dr_flac__reload_cache(bs)) { 83742 return MA_FALSE; 83743 } 83744 if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83745 return MA_FALSE; 83746 } 83747 bs_cache = bs->cache; 83748 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; 83749 } 83750 riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); 83751 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; 83752 bs_cache <<= riceParamPartLoBitCount; 83753 } 83754 } else { 83755 ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); 83756 for (;;) { 83757 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 83758 #ifndef MA_DR_FLAC_NO_CRC 83759 ma_dr_flac__update_crc16(bs); 83760 #endif 83761 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); 83762 bs_consumedBits = 0; 83763 #ifndef MA_DR_FLAC_NO_CRC 83764 bs->crc16Cache = bs_cache; 83765 #endif 83766 } else { 83767 if (!ma_dr_flac__reload_cache(bs)) { 83768 return MA_FALSE; 83769 } 83770 bs_cache = bs->cache; 83771 bs_consumedBits = bs->consumedBits; 83772 } 83773 lzcount = ma_dr_flac__clz(bs_cache); 83774 zeroCounter += lzcount; 83775 if (lzcount < sizeof(bs_cache)*8) { 83776 break; 83777 } 83778 } 83779 pZeroCounterOut[0] = zeroCounter; 83780 goto extract_rice_param_part; 83781 } 83782 bs->cache = bs_cache; 83783 bs->consumedBits = bs_consumedBits; 83784 return MA_TRUE; 83785 } 83786 static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) 83787 { 83788 ma_uint32 riceParamPlus1 = riceParam + 1; 83789 ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; 83790 ma_dr_flac_cache_t bs_cache = bs->cache; 83791 ma_uint32 bs_consumedBits = bs->consumedBits; 83792 ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); 83793 if (lzcount < sizeof(bs_cache)*8) { 83794 extract_rice_param_part: 83795 bs_cache <<= lzcount; 83796 bs_consumedBits += lzcount; 83797 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { 83798 bs_cache <<= riceParamPlus1; 83799 bs_consumedBits += riceParamPlus1; 83800 } else { 83801 ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; 83802 MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); 83803 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 83804 #ifndef MA_DR_FLAC_NO_CRC 83805 ma_dr_flac__update_crc16(bs); 83806 #endif 83807 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); 83808 bs_consumedBits = riceParamPartLoBitCount; 83809 #ifndef MA_DR_FLAC_NO_CRC 83810 bs->crc16Cache = bs_cache; 83811 #endif 83812 } else { 83813 if (!ma_dr_flac__reload_cache(bs)) { 83814 return MA_FALSE; 83815 } 83816 if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { 83817 return MA_FALSE; 83818 } 83819 bs_cache = bs->cache; 83820 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; 83821 } 83822 bs_cache <<= riceParamPartLoBitCount; 83823 } 83824 } else { 83825 for (;;) { 83826 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { 83827 #ifndef MA_DR_FLAC_NO_CRC 83828 ma_dr_flac__update_crc16(bs); 83829 #endif 83830 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); 83831 bs_consumedBits = 0; 83832 #ifndef MA_DR_FLAC_NO_CRC 83833 bs->crc16Cache = bs_cache; 83834 #endif 83835 } else { 83836 if (!ma_dr_flac__reload_cache(bs)) { 83837 return MA_FALSE; 83838 } 83839 bs_cache = bs->cache; 83840 bs_consumedBits = bs->consumedBits; 83841 } 83842 lzcount = ma_dr_flac__clz(bs_cache); 83843 if (lzcount < sizeof(bs_cache)*8) { 83844 break; 83845 } 83846 } 83847 goto extract_rice_param_part; 83848 } 83849 bs->cache = bs_cache; 83850 bs->consumedBits = bs_consumedBits; 83851 return MA_TRUE; 83852 } 83853 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) 83854 { 83855 ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 83856 ma_uint32 zeroCountPart0; 83857 ma_uint32 riceParamPart0; 83858 ma_uint32 riceParamMask; 83859 ma_uint32 i; 83860 MA_DR_FLAC_ASSERT(bs != NULL); 83861 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 83862 (void)bitsPerSample; 83863 (void)order; 83864 (void)shift; 83865 (void)coefficients; 83866 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 83867 i = 0; 83868 while (i < count) { 83869 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { 83870 return MA_FALSE; 83871 } 83872 riceParamPart0 &= riceParamMask; 83873 riceParamPart0 |= (zeroCountPart0 << riceParam); 83874 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; 83875 pSamplesOut[i] = riceParamPart0; 83876 i += 1; 83877 } 83878 return MA_TRUE; 83879 } 83880 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 83881 { 83882 ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 83883 ma_uint32 zeroCountPart0 = 0; 83884 ma_uint32 zeroCountPart1 = 0; 83885 ma_uint32 zeroCountPart2 = 0; 83886 ma_uint32 zeroCountPart3 = 0; 83887 ma_uint32 riceParamPart0 = 0; 83888 ma_uint32 riceParamPart1 = 0; 83889 ma_uint32 riceParamPart2 = 0; 83890 ma_uint32 riceParamPart3 = 0; 83891 ma_uint32 riceParamMask; 83892 const ma_int32* pSamplesOutEnd; 83893 ma_uint32 i; 83894 MA_DR_FLAC_ASSERT(bs != NULL); 83895 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 83896 if (lpcOrder == 0) { 83897 return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); 83898 } 83899 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 83900 pSamplesOutEnd = pSamplesOut + (count & ~3); 83901 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 83902 while (pSamplesOut < pSamplesOutEnd) { 83903 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || 83904 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || 83905 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || 83906 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { 83907 return MA_FALSE; 83908 } 83909 riceParamPart0 &= riceParamMask; 83910 riceParamPart1 &= riceParamMask; 83911 riceParamPart2 &= riceParamMask; 83912 riceParamPart3 &= riceParamMask; 83913 riceParamPart0 |= (zeroCountPart0 << riceParam); 83914 riceParamPart1 |= (zeroCountPart1 << riceParam); 83915 riceParamPart2 |= (zeroCountPart2 << riceParam); 83916 riceParamPart3 |= (zeroCountPart3 << riceParam); 83917 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; 83918 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; 83919 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; 83920 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; 83921 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); 83922 pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); 83923 pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); 83924 pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); 83925 pSamplesOut += 4; 83926 } 83927 } else { 83928 while (pSamplesOut < pSamplesOutEnd) { 83929 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || 83930 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || 83931 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || 83932 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { 83933 return MA_FALSE; 83934 } 83935 riceParamPart0 &= riceParamMask; 83936 riceParamPart1 &= riceParamMask; 83937 riceParamPart2 &= riceParamMask; 83938 riceParamPart3 &= riceParamMask; 83939 riceParamPart0 |= (zeroCountPart0 << riceParam); 83940 riceParamPart1 |= (zeroCountPart1 << riceParam); 83941 riceParamPart2 |= (zeroCountPart2 << riceParam); 83942 riceParamPart3 |= (zeroCountPart3 << riceParam); 83943 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; 83944 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; 83945 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; 83946 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; 83947 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); 83948 pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); 83949 pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); 83950 pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); 83951 pSamplesOut += 4; 83952 } 83953 } 83954 i = (count & ~3); 83955 while (i < count) { 83956 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { 83957 return MA_FALSE; 83958 } 83959 riceParamPart0 &= riceParamMask; 83960 riceParamPart0 |= (zeroCountPart0 << riceParam); 83961 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; 83962 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 83963 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); 83964 } else { 83965 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); 83966 } 83967 i += 1; 83968 pSamplesOut += 1; 83969 } 83970 return MA_TRUE; 83971 } 83972 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 83973 static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) 83974 { 83975 __m128i r; 83976 r = _mm_packs_epi32(a, b); 83977 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); 83978 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); 83979 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); 83980 return r; 83981 } 83982 #endif 83983 #if defined(MA_DR_FLAC_SUPPORT_SSE41) 83984 static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) 83985 { 83986 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); 83987 } 83988 static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) 83989 { 83990 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); 83991 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); 83992 return _mm_add_epi32(x64, x32); 83993 } 83994 static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) 83995 { 83996 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); 83997 } 83998 static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) 83999 { 84000 __m128i lo = _mm_srli_epi64(x, count); 84001 __m128i hi = _mm_srai_epi32(x, count); 84002 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); 84003 return _mm_or_si128(lo, hi); 84004 } 84005 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) 84006 { 84007 int i; 84008 ma_uint32 riceParamMask; 84009 ma_int32* pDecodedSamples = pSamplesOut; 84010 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); 84011 ma_uint32 zeroCountParts0 = 0; 84012 ma_uint32 zeroCountParts1 = 0; 84013 ma_uint32 zeroCountParts2 = 0; 84014 ma_uint32 zeroCountParts3 = 0; 84015 ma_uint32 riceParamParts0 = 0; 84016 ma_uint32 riceParamParts1 = 0; 84017 ma_uint32 riceParamParts2 = 0; 84018 ma_uint32 riceParamParts3 = 0; 84019 __m128i coefficients128_0; 84020 __m128i coefficients128_4; 84021 __m128i coefficients128_8; 84022 __m128i samples128_0; 84023 __m128i samples128_4; 84024 __m128i samples128_8; 84025 __m128i riceParamMask128; 84026 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 84027 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 84028 riceParamMask128 = _mm_set1_epi32(riceParamMask); 84029 coefficients128_0 = _mm_setzero_si128(); 84030 coefficients128_4 = _mm_setzero_si128(); 84031 coefficients128_8 = _mm_setzero_si128(); 84032 samples128_0 = _mm_setzero_si128(); 84033 samples128_4 = _mm_setzero_si128(); 84034 samples128_8 = _mm_setzero_si128(); 84035 #if 1 84036 { 84037 int runningOrder = order; 84038 if (runningOrder >= 4) { 84039 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); 84040 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); 84041 runningOrder -= 4; 84042 } else { 84043 switch (runningOrder) { 84044 case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; 84045 case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; 84046 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; 84047 } 84048 runningOrder = 0; 84049 } 84050 if (runningOrder >= 4) { 84051 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); 84052 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); 84053 runningOrder -= 4; 84054 } else { 84055 switch (runningOrder) { 84056 case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; 84057 case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; 84058 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; 84059 } 84060 runningOrder = 0; 84061 } 84062 if (runningOrder == 4) { 84063 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); 84064 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); 84065 runningOrder -= 4; 84066 } else { 84067 switch (runningOrder) { 84068 case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; 84069 case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; 84070 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; 84071 } 84072 runningOrder = 0; 84073 } 84074 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); 84075 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); 84076 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); 84077 } 84078 #else 84079 switch (order) 84080 { 84081 case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; 84082 case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; 84083 case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; 84084 case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; 84085 case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; 84086 case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; 84087 case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; 84088 case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; 84089 case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; 84090 case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; 84091 case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; 84092 case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; 84093 } 84094 #endif 84095 while (pDecodedSamples < pDecodedSamplesEnd) { 84096 __m128i prediction128; 84097 __m128i zeroCountPart128; 84098 __m128i riceParamPart128; 84099 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || 84100 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || 84101 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || 84102 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { 84103 return MA_FALSE; 84104 } 84105 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); 84106 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); 84107 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); 84108 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); 84109 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); 84110 if (order <= 4) { 84111 for (i = 0; i < 4; i += 1) { 84112 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); 84113 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); 84114 prediction128 = _mm_srai_epi32(prediction128, shift); 84115 prediction128 = _mm_add_epi32(riceParamPart128, prediction128); 84116 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); 84117 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); 84118 } 84119 } else if (order <= 8) { 84120 for (i = 0; i < 4; i += 1) { 84121 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); 84122 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); 84123 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); 84124 prediction128 = _mm_srai_epi32(prediction128, shift); 84125 prediction128 = _mm_add_epi32(riceParamPart128, prediction128); 84126 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); 84127 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); 84128 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); 84129 } 84130 } else { 84131 for (i = 0; i < 4; i += 1) { 84132 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); 84133 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); 84134 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); 84135 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); 84136 prediction128 = _mm_srai_epi32(prediction128, shift); 84137 prediction128 = _mm_add_epi32(riceParamPart128, prediction128); 84138 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); 84139 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); 84140 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); 84141 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); 84142 } 84143 } 84144 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); 84145 pDecodedSamples += 4; 84146 } 84147 i = (count & ~3); 84148 while (i < (int)count) { 84149 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { 84150 return MA_FALSE; 84151 } 84152 riceParamParts0 &= riceParamMask; 84153 riceParamParts0 |= (zeroCountParts0 << riceParam); 84154 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; 84155 pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); 84156 i += 1; 84157 pDecodedSamples += 1; 84158 } 84159 return MA_TRUE; 84160 } 84161 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) 84162 { 84163 int i; 84164 ma_uint32 riceParamMask; 84165 ma_int32* pDecodedSamples = pSamplesOut; 84166 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); 84167 ma_uint32 zeroCountParts0 = 0; 84168 ma_uint32 zeroCountParts1 = 0; 84169 ma_uint32 zeroCountParts2 = 0; 84170 ma_uint32 zeroCountParts3 = 0; 84171 ma_uint32 riceParamParts0 = 0; 84172 ma_uint32 riceParamParts1 = 0; 84173 ma_uint32 riceParamParts2 = 0; 84174 ma_uint32 riceParamParts3 = 0; 84175 __m128i coefficients128_0; 84176 __m128i coefficients128_4; 84177 __m128i coefficients128_8; 84178 __m128i samples128_0; 84179 __m128i samples128_4; 84180 __m128i samples128_8; 84181 __m128i prediction128; 84182 __m128i riceParamMask128; 84183 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 84184 MA_DR_FLAC_ASSERT(order <= 12); 84185 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 84186 riceParamMask128 = _mm_set1_epi32(riceParamMask); 84187 prediction128 = _mm_setzero_si128(); 84188 coefficients128_0 = _mm_setzero_si128(); 84189 coefficients128_4 = _mm_setzero_si128(); 84190 coefficients128_8 = _mm_setzero_si128(); 84191 samples128_0 = _mm_setzero_si128(); 84192 samples128_4 = _mm_setzero_si128(); 84193 samples128_8 = _mm_setzero_si128(); 84194 #if 1 84195 { 84196 int runningOrder = order; 84197 if (runningOrder >= 4) { 84198 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); 84199 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); 84200 runningOrder -= 4; 84201 } else { 84202 switch (runningOrder) { 84203 case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; 84204 case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; 84205 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; 84206 } 84207 runningOrder = 0; 84208 } 84209 if (runningOrder >= 4) { 84210 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); 84211 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); 84212 runningOrder -= 4; 84213 } else { 84214 switch (runningOrder) { 84215 case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; 84216 case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; 84217 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; 84218 } 84219 runningOrder = 0; 84220 } 84221 if (runningOrder == 4) { 84222 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); 84223 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); 84224 runningOrder -= 4; 84225 } else { 84226 switch (runningOrder) { 84227 case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; 84228 case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; 84229 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; 84230 } 84231 runningOrder = 0; 84232 } 84233 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); 84234 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); 84235 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); 84236 } 84237 #else 84238 switch (order) 84239 { 84240 case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; 84241 case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; 84242 case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; 84243 case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; 84244 case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; 84245 case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; 84246 case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; 84247 case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; 84248 case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; 84249 case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; 84250 case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; 84251 case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; 84252 } 84253 #endif 84254 while (pDecodedSamples < pDecodedSamplesEnd) { 84255 __m128i zeroCountPart128; 84256 __m128i riceParamPart128; 84257 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || 84258 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || 84259 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || 84260 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { 84261 return MA_FALSE; 84262 } 84263 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); 84264 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); 84265 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); 84266 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); 84267 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); 84268 for (i = 0; i < 4; i += 1) { 84269 prediction128 = _mm_xor_si128(prediction128, prediction128); 84270 switch (order) 84271 { 84272 case 12: 84273 case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); 84274 case 10: 84275 case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); 84276 case 8: 84277 case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); 84278 case 6: 84279 case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); 84280 case 4: 84281 case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); 84282 case 2: 84283 case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); 84284 } 84285 prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); 84286 prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); 84287 prediction128 = _mm_add_epi32(riceParamPart128, prediction128); 84288 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); 84289 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); 84290 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); 84291 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); 84292 } 84293 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); 84294 pDecodedSamples += 4; 84295 } 84296 i = (count & ~3); 84297 while (i < (int)count) { 84298 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { 84299 return MA_FALSE; 84300 } 84301 riceParamParts0 &= riceParamMask; 84302 riceParamParts0 |= (zeroCountParts0 << riceParam); 84303 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; 84304 pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); 84305 i += 1; 84306 pDecodedSamples += 1; 84307 } 84308 return MA_TRUE; 84309 } 84310 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 84311 { 84312 MA_DR_FLAC_ASSERT(bs != NULL); 84313 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 84314 if (lpcOrder > 0 && lpcOrder <= 12) { 84315 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 84316 return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); 84317 } else { 84318 return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); 84319 } 84320 } else { 84321 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84322 } 84323 } 84324 #endif 84325 #if defined(MA_DR_FLAC_SUPPORT_NEON) 84326 static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) 84327 { 84328 vst1q_s32(p+0, x.val[0]); 84329 vst1q_s32(p+4, x.val[1]); 84330 } 84331 static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) 84332 { 84333 vst1q_u32(p+0, x.val[0]); 84334 vst1q_u32(p+4, x.val[1]); 84335 } 84336 static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) 84337 { 84338 vst1q_f32(p+0, x.val[0]); 84339 vst1q_f32(p+4, x.val[1]); 84340 } 84341 static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) 84342 { 84343 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); 84344 } 84345 static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) 84346 { 84347 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); 84348 } 84349 static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) 84350 { 84351 ma_int32 x[4]; 84352 x[3] = x3; 84353 x[2] = x2; 84354 x[1] = x1; 84355 x[0] = x0; 84356 return vld1q_s32(x); 84357 } 84358 static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) 84359 { 84360 return vextq_s32(b, a, 1); 84361 } 84362 static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) 84363 { 84364 return vextq_u32(b, a, 1); 84365 } 84366 static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) 84367 { 84368 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); 84369 return vpadd_s32(r, r); 84370 } 84371 static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) 84372 { 84373 return vadd_s64(vget_high_s64(x), vget_low_s64(x)); 84374 } 84375 static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) 84376 { 84377 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); 84378 } 84379 static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) 84380 { 84381 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); 84382 } 84383 static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) 84384 { 84385 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); 84386 } 84387 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) 84388 { 84389 int i; 84390 ma_uint32 riceParamMask; 84391 ma_int32* pDecodedSamples = pSamplesOut; 84392 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); 84393 ma_uint32 zeroCountParts[4]; 84394 ma_uint32 riceParamParts[4]; 84395 int32x4_t coefficients128_0; 84396 int32x4_t coefficients128_4; 84397 int32x4_t coefficients128_8; 84398 int32x4_t samples128_0; 84399 int32x4_t samples128_4; 84400 int32x4_t samples128_8; 84401 uint32x4_t riceParamMask128; 84402 int32x4_t riceParam128; 84403 int32x2_t shift64; 84404 uint32x4_t one128; 84405 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 84406 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 84407 riceParamMask128 = vdupq_n_u32(riceParamMask); 84408 riceParam128 = vdupq_n_s32(riceParam); 84409 shift64 = vdup_n_s32(-shift); 84410 one128 = vdupq_n_u32(1); 84411 { 84412 int runningOrder = order; 84413 ma_int32 tempC[4] = {0, 0, 0, 0}; 84414 ma_int32 tempS[4] = {0, 0, 0, 0}; 84415 if (runningOrder >= 4) { 84416 coefficients128_0 = vld1q_s32(coefficients + 0); 84417 samples128_0 = vld1q_s32(pSamplesOut - 4); 84418 runningOrder -= 4; 84419 } else { 84420 switch (runningOrder) { 84421 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; 84422 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; 84423 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; 84424 } 84425 coefficients128_0 = vld1q_s32(tempC); 84426 samples128_0 = vld1q_s32(tempS); 84427 runningOrder = 0; 84428 } 84429 if (runningOrder >= 4) { 84430 coefficients128_4 = vld1q_s32(coefficients + 4); 84431 samples128_4 = vld1q_s32(pSamplesOut - 8); 84432 runningOrder -= 4; 84433 } else { 84434 switch (runningOrder) { 84435 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; 84436 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; 84437 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; 84438 } 84439 coefficients128_4 = vld1q_s32(tempC); 84440 samples128_4 = vld1q_s32(tempS); 84441 runningOrder = 0; 84442 } 84443 if (runningOrder == 4) { 84444 coefficients128_8 = vld1q_s32(coefficients + 8); 84445 samples128_8 = vld1q_s32(pSamplesOut - 12); 84446 runningOrder -= 4; 84447 } else { 84448 switch (runningOrder) { 84449 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; 84450 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; 84451 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; 84452 } 84453 coefficients128_8 = vld1q_s32(tempC); 84454 samples128_8 = vld1q_s32(tempS); 84455 runningOrder = 0; 84456 } 84457 coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); 84458 coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); 84459 coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); 84460 } 84461 while (pDecodedSamples < pDecodedSamplesEnd) { 84462 int32x4_t prediction128; 84463 int32x2_t prediction64; 84464 uint32x4_t zeroCountPart128; 84465 uint32x4_t riceParamPart128; 84466 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || 84467 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || 84468 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || 84469 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { 84470 return MA_FALSE; 84471 } 84472 zeroCountPart128 = vld1q_u32(zeroCountParts); 84473 riceParamPart128 = vld1q_u32(riceParamParts); 84474 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); 84475 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); 84476 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); 84477 if (order <= 4) { 84478 for (i = 0; i < 4; i += 1) { 84479 prediction128 = vmulq_s32(coefficients128_0, samples128_0); 84480 prediction64 = ma_dr_flac__vhaddq_s32(prediction128); 84481 prediction64 = vshl_s32(prediction64, shift64); 84482 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); 84483 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); 84484 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); 84485 } 84486 } else if (order <= 8) { 84487 for (i = 0; i < 4; i += 1) { 84488 prediction128 = vmulq_s32(coefficients128_4, samples128_4); 84489 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); 84490 prediction64 = ma_dr_flac__vhaddq_s32(prediction128); 84491 prediction64 = vshl_s32(prediction64, shift64); 84492 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); 84493 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); 84494 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); 84495 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); 84496 } 84497 } else { 84498 for (i = 0; i < 4; i += 1) { 84499 prediction128 = vmulq_s32(coefficients128_8, samples128_8); 84500 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); 84501 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); 84502 prediction64 = ma_dr_flac__vhaddq_s32(prediction128); 84503 prediction64 = vshl_s32(prediction64, shift64); 84504 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); 84505 samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); 84506 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); 84507 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); 84508 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); 84509 } 84510 } 84511 vst1q_s32(pDecodedSamples, samples128_0); 84512 pDecodedSamples += 4; 84513 } 84514 i = (count & ~3); 84515 while (i < (int)count) { 84516 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { 84517 return MA_FALSE; 84518 } 84519 riceParamParts[0] &= riceParamMask; 84520 riceParamParts[0] |= (zeroCountParts[0] << riceParam); 84521 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; 84522 pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); 84523 i += 1; 84524 pDecodedSamples += 1; 84525 } 84526 return MA_TRUE; 84527 } 84528 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) 84529 { 84530 int i; 84531 ma_uint32 riceParamMask; 84532 ma_int32* pDecodedSamples = pSamplesOut; 84533 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); 84534 ma_uint32 zeroCountParts[4]; 84535 ma_uint32 riceParamParts[4]; 84536 int32x4_t coefficients128_0; 84537 int32x4_t coefficients128_4; 84538 int32x4_t coefficients128_8; 84539 int32x4_t samples128_0; 84540 int32x4_t samples128_4; 84541 int32x4_t samples128_8; 84542 uint32x4_t riceParamMask128; 84543 int32x4_t riceParam128; 84544 int64x1_t shift64; 84545 uint32x4_t one128; 84546 int64x2_t prediction128 = { 0 }; 84547 uint32x4_t zeroCountPart128; 84548 uint32x4_t riceParamPart128; 84549 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; 84550 riceParamMask = (ma_uint32)~((~0UL) << riceParam); 84551 riceParamMask128 = vdupq_n_u32(riceParamMask); 84552 riceParam128 = vdupq_n_s32(riceParam); 84553 shift64 = vdup_n_s64(-shift); 84554 one128 = vdupq_n_u32(1); 84555 { 84556 int runningOrder = order; 84557 ma_int32 tempC[4] = {0, 0, 0, 0}; 84558 ma_int32 tempS[4] = {0, 0, 0, 0}; 84559 if (runningOrder >= 4) { 84560 coefficients128_0 = vld1q_s32(coefficients + 0); 84561 samples128_0 = vld1q_s32(pSamplesOut - 4); 84562 runningOrder -= 4; 84563 } else { 84564 switch (runningOrder) { 84565 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; 84566 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; 84567 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; 84568 } 84569 coefficients128_0 = vld1q_s32(tempC); 84570 samples128_0 = vld1q_s32(tempS); 84571 runningOrder = 0; 84572 } 84573 if (runningOrder >= 4) { 84574 coefficients128_4 = vld1q_s32(coefficients + 4); 84575 samples128_4 = vld1q_s32(pSamplesOut - 8); 84576 runningOrder -= 4; 84577 } else { 84578 switch (runningOrder) { 84579 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; 84580 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; 84581 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; 84582 } 84583 coefficients128_4 = vld1q_s32(tempC); 84584 samples128_4 = vld1q_s32(tempS); 84585 runningOrder = 0; 84586 } 84587 if (runningOrder == 4) { 84588 coefficients128_8 = vld1q_s32(coefficients + 8); 84589 samples128_8 = vld1q_s32(pSamplesOut - 12); 84590 runningOrder -= 4; 84591 } else { 84592 switch (runningOrder) { 84593 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; 84594 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; 84595 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; 84596 } 84597 coefficients128_8 = vld1q_s32(tempC); 84598 samples128_8 = vld1q_s32(tempS); 84599 runningOrder = 0; 84600 } 84601 coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); 84602 coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); 84603 coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); 84604 } 84605 while (pDecodedSamples < pDecodedSamplesEnd) { 84606 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || 84607 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || 84608 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || 84609 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { 84610 return MA_FALSE; 84611 } 84612 zeroCountPart128 = vld1q_u32(zeroCountParts); 84613 riceParamPart128 = vld1q_u32(riceParamParts); 84614 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); 84615 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); 84616 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); 84617 for (i = 0; i < 4; i += 1) { 84618 int64x1_t prediction64; 84619 prediction128 = veorq_s64(prediction128, prediction128); 84620 switch (order) 84621 { 84622 case 12: 84623 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); 84624 case 10: 84625 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); 84626 case 8: 84627 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); 84628 case 6: 84629 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); 84630 case 4: 84631 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); 84632 case 2: 84633 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); 84634 } 84635 prediction64 = ma_dr_flac__vhaddq_s64(prediction128); 84636 prediction64 = vshl_s64(prediction64, shift64); 84637 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); 84638 samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); 84639 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); 84640 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); 84641 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); 84642 } 84643 vst1q_s32(pDecodedSamples, samples128_0); 84644 pDecodedSamples += 4; 84645 } 84646 i = (count & ~3); 84647 while (i < (int)count) { 84648 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { 84649 return MA_FALSE; 84650 } 84651 riceParamParts[0] &= riceParamMask; 84652 riceParamParts[0] |= (zeroCountParts[0] << riceParam); 84653 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; 84654 pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); 84655 i += 1; 84656 pDecodedSamples += 1; 84657 } 84658 return MA_TRUE; 84659 } 84660 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 84661 { 84662 MA_DR_FLAC_ASSERT(bs != NULL); 84663 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 84664 if (lpcOrder > 0 && lpcOrder <= 12) { 84665 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 84666 return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); 84667 } else { 84668 return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); 84669 } 84670 } else { 84671 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84672 } 84673 } 84674 #endif 84675 static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 84676 { 84677 #if defined(MA_DR_FLAC_SUPPORT_SSE41) 84678 if (ma_dr_flac__gIsSSE41Supported) { 84679 return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84680 } else 84681 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 84682 if (ma_dr_flac__gIsNEONSupported) { 84683 return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84684 } else 84685 #endif 84686 { 84687 #if 0 84688 return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84689 #else 84690 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); 84691 #endif 84692 } 84693 } 84694 static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) 84695 { 84696 ma_uint32 i; 84697 MA_DR_FLAC_ASSERT(bs != NULL); 84698 for (i = 0; i < count; ++i) { 84699 if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { 84700 return MA_FALSE; 84701 } 84702 } 84703 return MA_TRUE; 84704 } 84705 #if defined(__clang__) 84706 __attribute__((no_sanitize("signed-integer-overflow"))) 84707 #endif 84708 static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) 84709 { 84710 ma_uint32 i; 84711 MA_DR_FLAC_ASSERT(bs != NULL); 84712 MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); 84713 MA_DR_FLAC_ASSERT(pSamplesOut != NULL); 84714 for (i = 0; i < count; ++i) { 84715 if (unencodedBitsPerSample > 0) { 84716 if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { 84717 return MA_FALSE; 84718 } 84719 } else { 84720 pSamplesOut[i] = 0; 84721 } 84722 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { 84723 pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); 84724 } else { 84725 pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); 84726 } 84727 } 84728 return MA_TRUE; 84729 } 84730 static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) 84731 { 84732 ma_uint8 residualMethod; 84733 ma_uint8 partitionOrder; 84734 ma_uint32 samplesInPartition; 84735 ma_uint32 partitionsRemaining; 84736 MA_DR_FLAC_ASSERT(bs != NULL); 84737 MA_DR_FLAC_ASSERT(blockSize != 0); 84738 MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); 84739 if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { 84740 return MA_FALSE; 84741 } 84742 if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { 84743 return MA_FALSE; 84744 } 84745 pDecodedSamples += lpcOrder; 84746 if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { 84747 return MA_FALSE; 84748 } 84749 if (partitionOrder > 8) { 84750 return MA_FALSE; 84751 } 84752 if ((blockSize / (1 << partitionOrder)) < lpcOrder) { 84753 return MA_FALSE; 84754 } 84755 samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; 84756 partitionsRemaining = (1 << partitionOrder); 84757 for (;;) { 84758 ma_uint8 riceParam = 0; 84759 if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { 84760 if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { 84761 return MA_FALSE; 84762 } 84763 if (riceParam == 15) { 84764 riceParam = 0xFF; 84765 } 84766 } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { 84767 if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { 84768 return MA_FALSE; 84769 } 84770 if (riceParam == 31) { 84771 riceParam = 0xFF; 84772 } 84773 } 84774 if (riceParam != 0xFF) { 84775 if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { 84776 return MA_FALSE; 84777 } 84778 } else { 84779 ma_uint8 unencodedBitsPerSample = 0; 84780 if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { 84781 return MA_FALSE; 84782 } 84783 if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { 84784 return MA_FALSE; 84785 } 84786 } 84787 pDecodedSamples += samplesInPartition; 84788 if (partitionsRemaining == 1) { 84789 break; 84790 } 84791 partitionsRemaining -= 1; 84792 if (partitionOrder != 0) { 84793 samplesInPartition = blockSize / (1 << partitionOrder); 84794 } 84795 } 84796 return MA_TRUE; 84797 } 84798 static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) 84799 { 84800 ma_uint8 residualMethod; 84801 ma_uint8 partitionOrder; 84802 ma_uint32 samplesInPartition; 84803 ma_uint32 partitionsRemaining; 84804 MA_DR_FLAC_ASSERT(bs != NULL); 84805 MA_DR_FLAC_ASSERT(blockSize != 0); 84806 if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { 84807 return MA_FALSE; 84808 } 84809 if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { 84810 return MA_FALSE; 84811 } 84812 if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { 84813 return MA_FALSE; 84814 } 84815 if (partitionOrder > 8) { 84816 return MA_FALSE; 84817 } 84818 if ((blockSize / (1 << partitionOrder)) <= order) { 84819 return MA_FALSE; 84820 } 84821 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; 84822 partitionsRemaining = (1 << partitionOrder); 84823 for (;;) 84824 { 84825 ma_uint8 riceParam = 0; 84826 if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { 84827 if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { 84828 return MA_FALSE; 84829 } 84830 if (riceParam == 15) { 84831 riceParam = 0xFF; 84832 } 84833 } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { 84834 if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { 84835 return MA_FALSE; 84836 } 84837 if (riceParam == 31) { 84838 riceParam = 0xFF; 84839 } 84840 } 84841 if (riceParam != 0xFF) { 84842 if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { 84843 return MA_FALSE; 84844 } 84845 } else { 84846 ma_uint8 unencodedBitsPerSample = 0; 84847 if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { 84848 return MA_FALSE; 84849 } 84850 if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { 84851 return MA_FALSE; 84852 } 84853 } 84854 if (partitionsRemaining == 1) { 84855 break; 84856 } 84857 partitionsRemaining -= 1; 84858 samplesInPartition = blockSize / (1 << partitionOrder); 84859 } 84860 return MA_TRUE; 84861 } 84862 static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) 84863 { 84864 ma_uint32 i; 84865 ma_int32 sample; 84866 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { 84867 return MA_FALSE; 84868 } 84869 for (i = 0; i < blockSize; ++i) { 84870 pDecodedSamples[i] = sample; 84871 } 84872 return MA_TRUE; 84873 } 84874 static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) 84875 { 84876 ma_uint32 i; 84877 for (i = 0; i < blockSize; ++i) { 84878 ma_int32 sample; 84879 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { 84880 return MA_FALSE; 84881 } 84882 pDecodedSamples[i] = sample; 84883 } 84884 return MA_TRUE; 84885 } 84886 static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) 84887 { 84888 ma_uint32 i; 84889 static ma_int32 lpcCoefficientsTable[5][4] = { 84890 {0, 0, 0, 0}, 84891 {1, 0, 0, 0}, 84892 {2, -1, 0, 0}, 84893 {3, -3, 1, 0}, 84894 {4, -6, 4, -1} 84895 }; 84896 for (i = 0; i < lpcOrder; ++i) { 84897 ma_int32 sample; 84898 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { 84899 return MA_FALSE; 84900 } 84901 pDecodedSamples[i] = sample; 84902 } 84903 if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { 84904 return MA_FALSE; 84905 } 84906 return MA_TRUE; 84907 } 84908 static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) 84909 { 84910 ma_uint8 i; 84911 ma_uint8 lpcPrecision; 84912 ma_int8 lpcShift; 84913 ma_int32 coefficients[32]; 84914 for (i = 0; i < lpcOrder; ++i) { 84915 ma_int32 sample; 84916 if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { 84917 return MA_FALSE; 84918 } 84919 pDecodedSamples[i] = sample; 84920 } 84921 if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { 84922 return MA_FALSE; 84923 } 84924 if (lpcPrecision == 15) { 84925 return MA_FALSE; 84926 } 84927 lpcPrecision += 1; 84928 if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { 84929 return MA_FALSE; 84930 } 84931 if (lpcShift < 0) { 84932 return MA_FALSE; 84933 } 84934 MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); 84935 for (i = 0; i < lpcOrder; ++i) { 84936 if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { 84937 return MA_FALSE; 84938 } 84939 } 84940 if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { 84941 return MA_FALSE; 84942 } 84943 return MA_TRUE; 84944 } 84945 static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) 84946 { 84947 const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; 84948 const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; 84949 MA_DR_FLAC_ASSERT(bs != NULL); 84950 MA_DR_FLAC_ASSERT(header != NULL); 84951 for (;;) { 84952 ma_uint8 crc8 = 0xCE; 84953 ma_uint8 reserved = 0; 84954 ma_uint8 blockingStrategy = 0; 84955 ma_uint8 blockSize = 0; 84956 ma_uint8 sampleRate = 0; 84957 ma_uint8 channelAssignment = 0; 84958 ma_uint8 bitsPerSample = 0; 84959 ma_bool32 isVariableBlockSize; 84960 if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { 84961 return MA_FALSE; 84962 } 84963 if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { 84964 return MA_FALSE; 84965 } 84966 if (reserved == 1) { 84967 continue; 84968 } 84969 crc8 = ma_dr_flac_crc8(crc8, reserved, 1); 84970 if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { 84971 return MA_FALSE; 84972 } 84973 crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); 84974 if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { 84975 return MA_FALSE; 84976 } 84977 if (blockSize == 0) { 84978 continue; 84979 } 84980 crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); 84981 if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { 84982 return MA_FALSE; 84983 } 84984 crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); 84985 if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { 84986 return MA_FALSE; 84987 } 84988 if (channelAssignment > 10) { 84989 continue; 84990 } 84991 crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); 84992 if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { 84993 return MA_FALSE; 84994 } 84995 if (bitsPerSample == 3 || bitsPerSample == 7) { 84996 continue; 84997 } 84998 crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); 84999 if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { 85000 return MA_FALSE; 85001 } 85002 if (reserved == 1) { 85003 continue; 85004 } 85005 crc8 = ma_dr_flac_crc8(crc8, reserved, 1); 85006 isVariableBlockSize = blockingStrategy == 1; 85007 if (isVariableBlockSize) { 85008 ma_uint64 pcmFrameNumber; 85009 ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); 85010 if (result != MA_SUCCESS) { 85011 if (result == MA_AT_END) { 85012 return MA_FALSE; 85013 } else { 85014 continue; 85015 } 85016 } 85017 header->flacFrameNumber = 0; 85018 header->pcmFrameNumber = pcmFrameNumber; 85019 } else { 85020 ma_uint64 flacFrameNumber = 0; 85021 ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); 85022 if (result != MA_SUCCESS) { 85023 if (result == MA_AT_END) { 85024 return MA_FALSE; 85025 } else { 85026 continue; 85027 } 85028 } 85029 header->flacFrameNumber = (ma_uint32)flacFrameNumber; 85030 header->pcmFrameNumber = 0; 85031 } 85032 MA_DR_FLAC_ASSERT(blockSize > 0); 85033 if (blockSize == 1) { 85034 header->blockSizeInPCMFrames = 192; 85035 } else if (blockSize <= 5) { 85036 MA_DR_FLAC_ASSERT(blockSize >= 2); 85037 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); 85038 } else if (blockSize == 6) { 85039 if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { 85040 return MA_FALSE; 85041 } 85042 crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); 85043 header->blockSizeInPCMFrames += 1; 85044 } else if (blockSize == 7) { 85045 if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { 85046 return MA_FALSE; 85047 } 85048 crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); 85049 if (header->blockSizeInPCMFrames == 0xFFFF) { 85050 return MA_FALSE; 85051 } 85052 header->blockSizeInPCMFrames += 1; 85053 } else { 85054 MA_DR_FLAC_ASSERT(blockSize >= 8); 85055 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); 85056 } 85057 if (sampleRate <= 11) { 85058 header->sampleRate = sampleRateTable[sampleRate]; 85059 } else if (sampleRate == 12) { 85060 if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { 85061 return MA_FALSE; 85062 } 85063 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); 85064 header->sampleRate *= 1000; 85065 } else if (sampleRate == 13) { 85066 if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { 85067 return MA_FALSE; 85068 } 85069 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); 85070 } else if (sampleRate == 14) { 85071 if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { 85072 return MA_FALSE; 85073 } 85074 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); 85075 header->sampleRate *= 10; 85076 } else { 85077 continue; 85078 } 85079 header->channelAssignment = channelAssignment; 85080 header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; 85081 if (header->bitsPerSample == 0) { 85082 header->bitsPerSample = streaminfoBitsPerSample; 85083 } 85084 if (header->bitsPerSample != streaminfoBitsPerSample) { 85085 return MA_FALSE; 85086 } 85087 if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { 85088 return MA_FALSE; 85089 } 85090 #ifndef MA_DR_FLAC_NO_CRC 85091 if (header->crc8 != crc8) { 85092 continue; 85093 } 85094 #endif 85095 return MA_TRUE; 85096 } 85097 } 85098 static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) 85099 { 85100 ma_uint8 header; 85101 int type; 85102 if (!ma_dr_flac__read_uint8(bs, 8, &header)) { 85103 return MA_FALSE; 85104 } 85105 if ((header & 0x80) != 0) { 85106 return MA_FALSE; 85107 } 85108 type = (header & 0x7E) >> 1; 85109 if (type == 0) { 85110 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; 85111 } else if (type == 1) { 85112 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; 85113 } else { 85114 if ((type & 0x20) != 0) { 85115 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; 85116 pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; 85117 } else if ((type & 0x08) != 0) { 85118 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; 85119 pSubframe->lpcOrder = (ma_uint8)(type & 0x07); 85120 if (pSubframe->lpcOrder > 4) { 85121 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; 85122 pSubframe->lpcOrder = 0; 85123 } 85124 } else { 85125 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; 85126 } 85127 } 85128 if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { 85129 return MA_FALSE; 85130 } 85131 pSubframe->wastedBitsPerSample = 0; 85132 if ((header & 0x01) == 1) { 85133 unsigned int wastedBitsPerSample; 85134 if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { 85135 return MA_FALSE; 85136 } 85137 pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; 85138 } 85139 return MA_TRUE; 85140 } 85141 static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) 85142 { 85143 ma_dr_flac_subframe* pSubframe; 85144 ma_uint32 subframeBitsPerSample; 85145 MA_DR_FLAC_ASSERT(bs != NULL); 85146 MA_DR_FLAC_ASSERT(frame != NULL); 85147 pSubframe = frame->subframes + subframeIndex; 85148 if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { 85149 return MA_FALSE; 85150 } 85151 subframeBitsPerSample = frame->header.bitsPerSample; 85152 if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { 85153 subframeBitsPerSample += 1; 85154 } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { 85155 subframeBitsPerSample += 1; 85156 } 85157 if (subframeBitsPerSample > 32) { 85158 return MA_FALSE; 85159 } 85160 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { 85161 return MA_FALSE; 85162 } 85163 subframeBitsPerSample -= pSubframe->wastedBitsPerSample; 85164 pSubframe->pSamplesS32 = pDecodedSamplesOut; 85165 switch (pSubframe->subframeType) 85166 { 85167 case MA_DR_FLAC_SUBFRAME_CONSTANT: 85168 { 85169 ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); 85170 } break; 85171 case MA_DR_FLAC_SUBFRAME_VERBATIM: 85172 { 85173 ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); 85174 } break; 85175 case MA_DR_FLAC_SUBFRAME_FIXED: 85176 { 85177 ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); 85178 } break; 85179 case MA_DR_FLAC_SUBFRAME_LPC: 85180 { 85181 ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); 85182 } break; 85183 default: return MA_FALSE; 85184 } 85185 return MA_TRUE; 85186 } 85187 static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) 85188 { 85189 ma_dr_flac_subframe* pSubframe; 85190 ma_uint32 subframeBitsPerSample; 85191 MA_DR_FLAC_ASSERT(bs != NULL); 85192 MA_DR_FLAC_ASSERT(frame != NULL); 85193 pSubframe = frame->subframes + subframeIndex; 85194 if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { 85195 return MA_FALSE; 85196 } 85197 subframeBitsPerSample = frame->header.bitsPerSample; 85198 if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { 85199 subframeBitsPerSample += 1; 85200 } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { 85201 subframeBitsPerSample += 1; 85202 } 85203 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { 85204 return MA_FALSE; 85205 } 85206 subframeBitsPerSample -= pSubframe->wastedBitsPerSample; 85207 pSubframe->pSamplesS32 = NULL; 85208 switch (pSubframe->subframeType) 85209 { 85210 case MA_DR_FLAC_SUBFRAME_CONSTANT: 85211 { 85212 if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { 85213 return MA_FALSE; 85214 } 85215 } break; 85216 case MA_DR_FLAC_SUBFRAME_VERBATIM: 85217 { 85218 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; 85219 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { 85220 return MA_FALSE; 85221 } 85222 } break; 85223 case MA_DR_FLAC_SUBFRAME_FIXED: 85224 { 85225 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; 85226 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { 85227 return MA_FALSE; 85228 } 85229 if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { 85230 return MA_FALSE; 85231 } 85232 } break; 85233 case MA_DR_FLAC_SUBFRAME_LPC: 85234 { 85235 ma_uint8 lpcPrecision; 85236 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; 85237 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { 85238 return MA_FALSE; 85239 } 85240 if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { 85241 return MA_FALSE; 85242 } 85243 if (lpcPrecision == 15) { 85244 return MA_FALSE; 85245 } 85246 lpcPrecision += 1; 85247 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; 85248 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { 85249 return MA_FALSE; 85250 } 85251 if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { 85252 return MA_FALSE; 85253 } 85254 } break; 85255 default: return MA_FALSE; 85256 } 85257 return MA_TRUE; 85258 } 85259 static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) 85260 { 85261 ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; 85262 MA_DR_FLAC_ASSERT(channelAssignment <= 10); 85263 return lookup[channelAssignment]; 85264 } 85265 static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) 85266 { 85267 int channelCount; 85268 int i; 85269 ma_uint8 paddingSizeInBits; 85270 ma_uint16 desiredCRC16; 85271 #ifndef MA_DR_FLAC_NO_CRC 85272 ma_uint16 actualCRC16; 85273 #endif 85274 MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); 85275 if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { 85276 return MA_ERROR; 85277 } 85278 channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); 85279 if (channelCount != (int)pFlac->channels) { 85280 return MA_ERROR; 85281 } 85282 for (i = 0; i < channelCount; ++i) { 85283 if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { 85284 return MA_ERROR; 85285 } 85286 } 85287 paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); 85288 if (paddingSizeInBits > 0) { 85289 ma_uint8 padding = 0; 85290 if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { 85291 return MA_AT_END; 85292 } 85293 } 85294 #ifndef MA_DR_FLAC_NO_CRC 85295 actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); 85296 #endif 85297 if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { 85298 return MA_AT_END; 85299 } 85300 #ifndef MA_DR_FLAC_NO_CRC 85301 if (actualCRC16 != desiredCRC16) { 85302 return MA_CRC_MISMATCH; 85303 } 85304 #endif 85305 pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; 85306 return MA_SUCCESS; 85307 } 85308 static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) 85309 { 85310 int channelCount; 85311 int i; 85312 ma_uint16 desiredCRC16; 85313 #ifndef MA_DR_FLAC_NO_CRC 85314 ma_uint16 actualCRC16; 85315 #endif 85316 channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); 85317 for (i = 0; i < channelCount; ++i) { 85318 if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { 85319 return MA_ERROR; 85320 } 85321 } 85322 if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { 85323 return MA_ERROR; 85324 } 85325 #ifndef MA_DR_FLAC_NO_CRC 85326 actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); 85327 #endif 85328 if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { 85329 return MA_AT_END; 85330 } 85331 #ifndef MA_DR_FLAC_NO_CRC 85332 if (actualCRC16 != desiredCRC16) { 85333 return MA_CRC_MISMATCH; 85334 } 85335 #endif 85336 return MA_SUCCESS; 85337 } 85338 static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) 85339 { 85340 MA_DR_FLAC_ASSERT(pFlac != NULL); 85341 for (;;) { 85342 ma_result result; 85343 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85344 return MA_FALSE; 85345 } 85346 result = ma_dr_flac__decode_flac_frame(pFlac); 85347 if (result != MA_SUCCESS) { 85348 if (result == MA_CRC_MISMATCH) { 85349 continue; 85350 } else { 85351 return MA_FALSE; 85352 } 85353 } 85354 return MA_TRUE; 85355 } 85356 } 85357 static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) 85358 { 85359 ma_uint64 firstPCMFrame; 85360 ma_uint64 lastPCMFrame; 85361 MA_DR_FLAC_ASSERT(pFlac != NULL); 85362 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; 85363 if (firstPCMFrame == 0) { 85364 firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; 85365 } 85366 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; 85367 if (lastPCMFrame > 0) { 85368 lastPCMFrame -= 1; 85369 } 85370 if (pFirstPCMFrame) { 85371 *pFirstPCMFrame = firstPCMFrame; 85372 } 85373 if (pLastPCMFrame) { 85374 *pLastPCMFrame = lastPCMFrame; 85375 } 85376 } 85377 static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) 85378 { 85379 ma_bool32 result; 85380 MA_DR_FLAC_ASSERT(pFlac != NULL); 85381 result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); 85382 MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); 85383 pFlac->currentPCMFrame = 0; 85384 return result; 85385 } 85386 static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) 85387 { 85388 MA_DR_FLAC_ASSERT(pFlac != NULL); 85389 return ma_dr_flac__seek_flac_frame(pFlac); 85390 } 85391 static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) 85392 { 85393 ma_uint64 pcmFramesRead = 0; 85394 while (pcmFramesToSeek > 0) { 85395 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 85396 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { 85397 break; 85398 } 85399 } else { 85400 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { 85401 pcmFramesRead += pcmFramesToSeek; 85402 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; 85403 pcmFramesToSeek = 0; 85404 } else { 85405 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; 85406 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; 85407 pFlac->currentFLACFrame.pcmFramesRemaining = 0; 85408 } 85409 } 85410 } 85411 pFlac->currentPCMFrame += pcmFramesRead; 85412 return pcmFramesRead; 85413 } 85414 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) 85415 { 85416 ma_bool32 isMidFrame = MA_FALSE; 85417 ma_uint64 runningPCMFrameCount; 85418 MA_DR_FLAC_ASSERT(pFlac != NULL); 85419 if (pcmFrameIndex >= pFlac->currentPCMFrame) { 85420 runningPCMFrameCount = pFlac->currentPCMFrame; 85421 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 85422 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85423 return MA_FALSE; 85424 } 85425 } else { 85426 isMidFrame = MA_TRUE; 85427 } 85428 } else { 85429 runningPCMFrameCount = 0; 85430 if (!ma_dr_flac__seek_to_first_frame(pFlac)) { 85431 return MA_FALSE; 85432 } 85433 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85434 return MA_FALSE; 85435 } 85436 } 85437 for (;;) { 85438 ma_uint64 pcmFrameCountInThisFLACFrame; 85439 ma_uint64 firstPCMFrameInFLACFrame = 0; 85440 ma_uint64 lastPCMFrameInFLACFrame = 0; 85441 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); 85442 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; 85443 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { 85444 ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; 85445 if (!isMidFrame) { 85446 ma_result result = ma_dr_flac__decode_flac_frame(pFlac); 85447 if (result == MA_SUCCESS) { 85448 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; 85449 } else { 85450 if (result == MA_CRC_MISMATCH) { 85451 goto next_iteration; 85452 } else { 85453 return MA_FALSE; 85454 } 85455 } 85456 } else { 85457 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; 85458 } 85459 } else { 85460 if (!isMidFrame) { 85461 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); 85462 if (result == MA_SUCCESS) { 85463 runningPCMFrameCount += pcmFrameCountInThisFLACFrame; 85464 } else { 85465 if (result == MA_CRC_MISMATCH) { 85466 goto next_iteration; 85467 } else { 85468 return MA_FALSE; 85469 } 85470 } 85471 } else { 85472 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; 85473 pFlac->currentFLACFrame.pcmFramesRemaining = 0; 85474 isMidFrame = MA_FALSE; 85475 } 85476 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { 85477 return MA_TRUE; 85478 } 85479 } 85480 next_iteration: 85481 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85482 return MA_FALSE; 85483 } 85484 } 85485 } 85486 #if !defined(MA_DR_FLAC_NO_CRC) 85487 #define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f 85488 static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) 85489 { 85490 MA_DR_FLAC_ASSERT(pFlac != NULL); 85491 MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); 85492 MA_DR_FLAC_ASSERT(targetByte >= rangeLo); 85493 MA_DR_FLAC_ASSERT(targetByte <= rangeHi); 85494 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; 85495 for (;;) { 85496 ma_uint64 lastTargetByte = targetByte; 85497 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { 85498 if (targetByte == 0) { 85499 ma_dr_flac__seek_to_first_frame(pFlac); 85500 return MA_FALSE; 85501 } 85502 targetByte = rangeLo + ((rangeHi - rangeLo)/2); 85503 rangeHi = targetByte; 85504 } else { 85505 MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); 85506 #if 1 85507 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { 85508 targetByte = rangeLo + ((rangeHi - rangeLo)/2); 85509 rangeHi = targetByte; 85510 } else { 85511 break; 85512 } 85513 #else 85514 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85515 targetByte = rangeLo + ((rangeHi - rangeLo)/2); 85516 rangeHi = targetByte; 85517 } else { 85518 break; 85519 } 85520 #endif 85521 } 85522 if(targetByte == lastTargetByte) { 85523 return MA_FALSE; 85524 } 85525 } 85526 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); 85527 MA_DR_FLAC_ASSERT(targetByte <= rangeHi); 85528 *pLastSuccessfulSeekOffset = targetByte; 85529 return MA_TRUE; 85530 } 85531 static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) 85532 { 85533 #if 0 85534 if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { 85535 if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { 85536 return MA_FALSE; 85537 } 85538 } 85539 #endif 85540 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; 85541 } 85542 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) 85543 { 85544 ma_uint64 targetByte; 85545 ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; 85546 ma_uint64 pcmRangeHi = 0; 85547 ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; 85548 ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; 85549 ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; 85550 targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); 85551 if (targetByte > byteRangeHi) { 85552 targetByte = byteRangeHi; 85553 } 85554 for (;;) { 85555 if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { 85556 ma_uint64 newPCMRangeLo; 85557 ma_uint64 newPCMRangeHi; 85558 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); 85559 if (pcmRangeLo == newPCMRangeLo) { 85560 if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { 85561 break; 85562 } 85563 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { 85564 return MA_TRUE; 85565 } else { 85566 break; 85567 } 85568 } 85569 pcmRangeLo = newPCMRangeLo; 85570 pcmRangeHi = newPCMRangeHi; 85571 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { 85572 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { 85573 return MA_TRUE; 85574 } else { 85575 break; 85576 } 85577 } else { 85578 const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); 85579 if (pcmRangeLo > pcmFrameIndex) { 85580 byteRangeHi = lastSuccessfulSeekOffset; 85581 if (byteRangeLo > byteRangeHi) { 85582 byteRangeLo = byteRangeHi; 85583 } 85584 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); 85585 if (targetByte < byteRangeLo) { 85586 targetByte = byteRangeLo; 85587 } 85588 } else { 85589 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { 85590 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { 85591 return MA_TRUE; 85592 } else { 85593 break; 85594 } 85595 } else { 85596 byteRangeLo = lastSuccessfulSeekOffset; 85597 if (byteRangeHi < byteRangeLo) { 85598 byteRangeHi = byteRangeLo; 85599 } 85600 targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); 85601 if (targetByte > byteRangeHi) { 85602 targetByte = byteRangeHi; 85603 } 85604 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { 85605 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; 85606 } 85607 } 85608 } 85609 } 85610 } else { 85611 break; 85612 } 85613 } 85614 ma_dr_flac__seek_to_first_frame(pFlac); 85615 return MA_FALSE; 85616 } 85617 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) 85618 { 85619 ma_uint64 byteRangeLo; 85620 ma_uint64 byteRangeHi; 85621 ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; 85622 if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { 85623 return MA_FALSE; 85624 } 85625 if (pcmFrameIndex < seekForwardThreshold) { 85626 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; 85627 } 85628 byteRangeLo = pFlac->firstFLACFramePosInBytes; 85629 byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); 85630 return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); 85631 } 85632 #endif 85633 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) 85634 { 85635 ma_uint32 iClosestSeekpoint = 0; 85636 ma_bool32 isMidFrame = MA_FALSE; 85637 ma_uint64 runningPCMFrameCount; 85638 ma_uint32 iSeekpoint; 85639 MA_DR_FLAC_ASSERT(pFlac != NULL); 85640 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { 85641 return MA_FALSE; 85642 } 85643 if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { 85644 return MA_FALSE; 85645 } 85646 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { 85647 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { 85648 break; 85649 } 85650 iClosestSeekpoint = iSeekpoint; 85651 } 85652 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { 85653 return MA_FALSE; 85654 } 85655 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { 85656 return MA_FALSE; 85657 } 85658 #if !defined(MA_DR_FLAC_NO_CRC) 85659 if (pFlac->totalPCMFrameCount > 0) { 85660 ma_uint64 byteRangeLo; 85661 ma_uint64 byteRangeHi; 85662 byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); 85663 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; 85664 if (iClosestSeekpoint < pFlac->seekpointCount-1) { 85665 ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; 85666 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { 85667 return MA_FALSE; 85668 } 85669 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { 85670 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; 85671 } 85672 } 85673 if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { 85674 if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85675 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); 85676 if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { 85677 return MA_TRUE; 85678 } 85679 } 85680 } 85681 } 85682 #endif 85683 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { 85684 runningPCMFrameCount = pFlac->currentPCMFrame; 85685 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 85686 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85687 return MA_FALSE; 85688 } 85689 } else { 85690 isMidFrame = MA_TRUE; 85691 } 85692 } else { 85693 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; 85694 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { 85695 return MA_FALSE; 85696 } 85697 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85698 return MA_FALSE; 85699 } 85700 } 85701 for (;;) { 85702 ma_uint64 pcmFrameCountInThisFLACFrame; 85703 ma_uint64 firstPCMFrameInFLACFrame = 0; 85704 ma_uint64 lastPCMFrameInFLACFrame = 0; 85705 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); 85706 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; 85707 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { 85708 ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; 85709 if (!isMidFrame) { 85710 ma_result result = ma_dr_flac__decode_flac_frame(pFlac); 85711 if (result == MA_SUCCESS) { 85712 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; 85713 } else { 85714 if (result == MA_CRC_MISMATCH) { 85715 goto next_iteration; 85716 } else { 85717 return MA_FALSE; 85718 } 85719 } 85720 } else { 85721 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; 85722 } 85723 } else { 85724 if (!isMidFrame) { 85725 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); 85726 if (result == MA_SUCCESS) { 85727 runningPCMFrameCount += pcmFrameCountInThisFLACFrame; 85728 } else { 85729 if (result == MA_CRC_MISMATCH) { 85730 goto next_iteration; 85731 } else { 85732 return MA_FALSE; 85733 } 85734 } 85735 } else { 85736 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; 85737 pFlac->currentFLACFrame.pcmFramesRemaining = 0; 85738 isMidFrame = MA_FALSE; 85739 } 85740 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { 85741 return MA_TRUE; 85742 } 85743 } 85744 next_iteration: 85745 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 85746 return MA_FALSE; 85747 } 85748 } 85749 } 85750 #ifndef MA_DR_FLAC_NO_OGG 85751 typedef struct 85752 { 85753 ma_uint8 capturePattern[4]; 85754 ma_uint8 structureVersion; 85755 ma_uint8 headerType; 85756 ma_uint64 granulePosition; 85757 ma_uint32 serialNumber; 85758 ma_uint32 sequenceNumber; 85759 ma_uint32 checksum; 85760 ma_uint8 segmentCount; 85761 ma_uint8 segmentTable[255]; 85762 } ma_dr_flac_ogg_page_header; 85763 #endif 85764 typedef struct 85765 { 85766 ma_dr_flac_read_proc onRead; 85767 ma_dr_flac_seek_proc onSeek; 85768 ma_dr_flac_meta_proc onMeta; 85769 ma_dr_flac_container container; 85770 void* pUserData; 85771 void* pUserDataMD; 85772 ma_uint32 sampleRate; 85773 ma_uint8 channels; 85774 ma_uint8 bitsPerSample; 85775 ma_uint64 totalPCMFrameCount; 85776 ma_uint16 maxBlockSizeInPCMFrames; 85777 ma_uint64 runningFilePos; 85778 ma_bool32 hasStreamInfoBlock; 85779 ma_bool32 hasMetadataBlocks; 85780 ma_dr_flac_bs bs; 85781 ma_dr_flac_frame_header firstFrameHeader; 85782 #ifndef MA_DR_FLAC_NO_OGG 85783 ma_uint32 oggSerial; 85784 ma_uint64 oggFirstBytePos; 85785 ma_dr_flac_ogg_page_header oggBosHeader; 85786 #endif 85787 } ma_dr_flac_init_info; 85788 static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) 85789 { 85790 blockHeader = ma_dr_flac__be2host_32(blockHeader); 85791 *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); 85792 *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); 85793 *blockSize = (blockHeader & 0x00FFFFFFUL); 85794 } 85795 static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) 85796 { 85797 ma_uint32 blockHeader; 85798 *blockSize = 0; 85799 if (onRead(pUserData, &blockHeader, 4) != 4) { 85800 return MA_FALSE; 85801 } 85802 ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); 85803 return MA_TRUE; 85804 } 85805 static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) 85806 { 85807 ma_uint32 blockSizes; 85808 ma_uint64 frameSizes = 0; 85809 ma_uint64 importantProps; 85810 ma_uint8 md5[16]; 85811 if (onRead(pUserData, &blockSizes, 4) != 4) { 85812 return MA_FALSE; 85813 } 85814 if (onRead(pUserData, &frameSizes, 6) != 6) { 85815 return MA_FALSE; 85816 } 85817 if (onRead(pUserData, &importantProps, 8) != 8) { 85818 return MA_FALSE; 85819 } 85820 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { 85821 return MA_FALSE; 85822 } 85823 blockSizes = ma_dr_flac__be2host_32(blockSizes); 85824 frameSizes = ma_dr_flac__be2host_64(frameSizes); 85825 importantProps = ma_dr_flac__be2host_64(importantProps); 85826 pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); 85827 pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); 85828 pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); 85829 pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); 85830 pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); 85831 pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; 85832 pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; 85833 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); 85834 MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); 85835 return MA_TRUE; 85836 } 85837 static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) 85838 { 85839 (void)pUserData; 85840 return MA_DR_FLAC_MALLOC(sz); 85841 } 85842 static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) 85843 { 85844 (void)pUserData; 85845 return MA_DR_FLAC_REALLOC(p, sz); 85846 } 85847 static void ma_dr_flac__free_default(void* p, void* pUserData) 85848 { 85849 (void)pUserData; 85850 MA_DR_FLAC_FREE(p); 85851 } 85852 static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 85853 { 85854 if (pAllocationCallbacks == NULL) { 85855 return NULL; 85856 } 85857 if (pAllocationCallbacks->onMalloc != NULL) { 85858 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); 85859 } 85860 if (pAllocationCallbacks->onRealloc != NULL) { 85861 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); 85862 } 85863 return NULL; 85864 } 85865 static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) 85866 { 85867 if (pAllocationCallbacks == NULL) { 85868 return NULL; 85869 } 85870 if (pAllocationCallbacks->onRealloc != NULL) { 85871 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); 85872 } 85873 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { 85874 void* p2; 85875 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); 85876 if (p2 == NULL) { 85877 return NULL; 85878 } 85879 if (p != NULL) { 85880 MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); 85881 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 85882 } 85883 return p2; 85884 } 85885 return NULL; 85886 } 85887 static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 85888 { 85889 if (p == NULL || pAllocationCallbacks == NULL) { 85890 return; 85891 } 85892 if (pAllocationCallbacks->onFree != NULL) { 85893 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 85894 } 85895 } 85896 static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) 85897 { 85898 ma_uint64 runningFilePos = 42; 85899 ma_uint64 seektablePos = 0; 85900 ma_uint32 seektableSize = 0; 85901 for (;;) { 85902 ma_dr_flac_metadata metadata; 85903 ma_uint8 isLastBlock = 0; 85904 ma_uint8 blockType = 0; 85905 ma_uint32 blockSize; 85906 if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { 85907 return MA_FALSE; 85908 } 85909 runningFilePos += 4; 85910 metadata.type = blockType; 85911 metadata.pRawData = NULL; 85912 metadata.rawDataSize = 0; 85913 switch (blockType) 85914 { 85915 case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: 85916 { 85917 if (blockSize < 4) { 85918 return MA_FALSE; 85919 } 85920 if (onMeta) { 85921 void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); 85922 if (pRawData == NULL) { 85923 return MA_FALSE; 85924 } 85925 if (onRead(pUserData, pRawData, blockSize) != blockSize) { 85926 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85927 return MA_FALSE; 85928 } 85929 metadata.pRawData = pRawData; 85930 metadata.rawDataSize = blockSize; 85931 metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); 85932 metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); 85933 metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); 85934 onMeta(pUserDataMD, &metadata); 85935 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85936 } 85937 } break; 85938 case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: 85939 { 85940 seektablePos = runningFilePos; 85941 seektableSize = blockSize; 85942 if (onMeta) { 85943 ma_uint32 seekpointCount; 85944 ma_uint32 iSeekpoint; 85945 void* pRawData; 85946 seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; 85947 pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); 85948 if (pRawData == NULL) { 85949 return MA_FALSE; 85950 } 85951 for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { 85952 ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; 85953 if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { 85954 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85955 return MA_FALSE; 85956 } 85957 pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); 85958 pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); 85959 pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); 85960 } 85961 metadata.pRawData = pRawData; 85962 metadata.rawDataSize = blockSize; 85963 metadata.data.seektable.seekpointCount = seekpointCount; 85964 metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; 85965 onMeta(pUserDataMD, &metadata); 85966 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85967 } 85968 } break; 85969 case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: 85970 { 85971 if (blockSize < 8) { 85972 return MA_FALSE; 85973 } 85974 if (onMeta) { 85975 void* pRawData; 85976 const char* pRunningData; 85977 const char* pRunningDataEnd; 85978 ma_uint32 i; 85979 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); 85980 if (pRawData == NULL) { 85981 return MA_FALSE; 85982 } 85983 if (onRead(pUserData, pRawData, blockSize) != blockSize) { 85984 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85985 return MA_FALSE; 85986 } 85987 metadata.pRawData = pRawData; 85988 metadata.rawDataSize = blockSize; 85989 pRunningData = (const char*)pRawData; 85990 pRunningDataEnd = (const char*)pRawData + blockSize; 85991 metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 85992 if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { 85993 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 85994 return MA_FALSE; 85995 } 85996 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; 85997 metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 85998 if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { 85999 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86000 return MA_FALSE; 86001 } 86002 metadata.data.vorbis_comment.pComments = pRunningData; 86003 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { 86004 ma_uint32 commentLength; 86005 if (pRunningDataEnd - pRunningData < 4) { 86006 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86007 return MA_FALSE; 86008 } 86009 commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86010 if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { 86011 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86012 return MA_FALSE; 86013 } 86014 pRunningData += commentLength; 86015 } 86016 onMeta(pUserDataMD, &metadata); 86017 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86018 } 86019 } break; 86020 case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: 86021 { 86022 if (blockSize < 396) { 86023 return MA_FALSE; 86024 } 86025 if (onMeta) { 86026 void* pRawData; 86027 const char* pRunningData; 86028 const char* pRunningDataEnd; 86029 size_t bufferSize; 86030 ma_uint8 iTrack; 86031 ma_uint8 iIndex; 86032 void* pTrackData; 86033 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); 86034 if (pRawData == NULL) { 86035 return MA_FALSE; 86036 } 86037 if (onRead(pUserData, pRawData, blockSize) != blockSize) { 86038 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86039 return MA_FALSE; 86040 } 86041 metadata.pRawData = pRawData; 86042 metadata.rawDataSize = blockSize; 86043 pRunningData = (const char*)pRawData; 86044 pRunningDataEnd = (const char*)pRawData + blockSize; 86045 MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; 86046 metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; 86047 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; 86048 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; 86049 metadata.data.cuesheet.pTrackData = NULL; 86050 { 86051 const char* pRunningDataSaved = pRunningData; 86052 bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; 86053 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { 86054 ma_uint8 indexCount; 86055 ma_uint32 indexPointSize; 86056 if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { 86057 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86058 return MA_FALSE; 86059 } 86060 pRunningData += 35; 86061 indexCount = pRunningData[0]; 86062 pRunningData += 1; 86063 bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); 86064 indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; 86065 if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { 86066 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86067 return MA_FALSE; 86068 } 86069 pRunningData += indexPointSize; 86070 } 86071 pRunningData = pRunningDataSaved; 86072 } 86073 { 86074 char* pRunningTrackData; 86075 pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); 86076 if (pTrackData == NULL) { 86077 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86078 return MA_FALSE; 86079 } 86080 pRunningTrackData = (char*)pTrackData; 86081 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { 86082 ma_uint8 indexCount; 86083 MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); 86084 pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; 86085 pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; 86086 indexCount = pRunningData[0]; 86087 pRunningData += 1; 86088 pRunningTrackData += 1; 86089 for (iIndex = 0; iIndex < indexCount; ++iIndex) { 86090 ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; 86091 MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); 86092 pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; 86093 pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); 86094 pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); 86095 } 86096 } 86097 metadata.data.cuesheet.pTrackData = pTrackData; 86098 } 86099 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86100 pRawData = NULL; 86101 onMeta(pUserDataMD, &metadata); 86102 ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); 86103 pTrackData = NULL; 86104 } 86105 } break; 86106 case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: 86107 { 86108 if (blockSize < 32) { 86109 return MA_FALSE; 86110 } 86111 if (onMeta) { 86112 void* pRawData; 86113 const char* pRunningData; 86114 const char* pRunningDataEnd; 86115 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); 86116 if (pRawData == NULL) { 86117 return MA_FALSE; 86118 } 86119 if (onRead(pUserData, pRawData, blockSize) != blockSize) { 86120 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86121 return MA_FALSE; 86122 } 86123 metadata.pRawData = pRawData; 86124 metadata.rawDataSize = blockSize; 86125 pRunningData = (const char*)pRawData; 86126 pRunningDataEnd = (const char*)pRawData + blockSize; 86127 metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86128 metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86129 if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { 86130 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86131 return MA_FALSE; 86132 } 86133 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; 86134 metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86135 if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { 86136 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86137 return MA_FALSE; 86138 } 86139 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; 86140 metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86141 metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86142 metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86143 metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86144 metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; 86145 metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; 86146 if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { 86147 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86148 return MA_FALSE; 86149 } 86150 onMeta(pUserDataMD, &metadata); 86151 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86152 } 86153 } break; 86154 case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: 86155 { 86156 if (onMeta) { 86157 metadata.data.padding.unused = 0; 86158 if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { 86159 isLastBlock = MA_TRUE; 86160 } else { 86161 onMeta(pUserDataMD, &metadata); 86162 } 86163 } 86164 } break; 86165 case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: 86166 { 86167 if (onMeta) { 86168 if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { 86169 isLastBlock = MA_TRUE; 86170 } 86171 } 86172 } break; 86173 default: 86174 { 86175 if (onMeta) { 86176 void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); 86177 if (pRawData == NULL) { 86178 return MA_FALSE; 86179 } 86180 if (onRead(pUserData, pRawData, blockSize) != blockSize) { 86181 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86182 return MA_FALSE; 86183 } 86184 metadata.pRawData = pRawData; 86185 metadata.rawDataSize = blockSize; 86186 onMeta(pUserDataMD, &metadata); 86187 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); 86188 } 86189 } break; 86190 } 86191 if (onMeta == NULL && blockSize > 0) { 86192 if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { 86193 isLastBlock = MA_TRUE; 86194 } 86195 } 86196 runningFilePos += blockSize; 86197 if (isLastBlock) { 86198 break; 86199 } 86200 } 86201 *pSeektablePos = seektablePos; 86202 *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; 86203 *pFirstFramePos = runningFilePos; 86204 return MA_TRUE; 86205 } 86206 static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) 86207 { 86208 ma_uint8 isLastBlock; 86209 ma_uint8 blockType; 86210 ma_uint32 blockSize; 86211 (void)onSeek; 86212 pInit->container = ma_dr_flac_container_native; 86213 if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { 86214 return MA_FALSE; 86215 } 86216 if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { 86217 if (!relaxed) { 86218 return MA_FALSE; 86219 } else { 86220 pInit->hasStreamInfoBlock = MA_FALSE; 86221 pInit->hasMetadataBlocks = MA_FALSE; 86222 if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { 86223 return MA_FALSE; 86224 } 86225 if (pInit->firstFrameHeader.bitsPerSample == 0) { 86226 return MA_FALSE; 86227 } 86228 pInit->sampleRate = pInit->firstFrameHeader.sampleRate; 86229 pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); 86230 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; 86231 pInit->maxBlockSizeInPCMFrames = 65535; 86232 return MA_TRUE; 86233 } 86234 } else { 86235 ma_dr_flac_streaminfo streaminfo; 86236 if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { 86237 return MA_FALSE; 86238 } 86239 pInit->hasStreamInfoBlock = MA_TRUE; 86240 pInit->sampleRate = streaminfo.sampleRate; 86241 pInit->channels = streaminfo.channels; 86242 pInit->bitsPerSample = streaminfo.bitsPerSample; 86243 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; 86244 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; 86245 pInit->hasMetadataBlocks = !isLastBlock; 86246 if (onMeta) { 86247 ma_dr_flac_metadata metadata; 86248 metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; 86249 metadata.pRawData = NULL; 86250 metadata.rawDataSize = 0; 86251 metadata.data.streaminfo = streaminfo; 86252 onMeta(pUserDataMD, &metadata); 86253 } 86254 return MA_TRUE; 86255 } 86256 } 86257 #ifndef MA_DR_FLAC_NO_OGG 86258 #define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 86259 #define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 86260 typedef enum 86261 { 86262 ma_dr_flac_ogg_recover_on_crc_mismatch, 86263 ma_dr_flac_ogg_fail_on_crc_mismatch 86264 } ma_dr_flac_ogg_crc_mismatch_recovery; 86265 #ifndef MA_DR_FLAC_NO_CRC 86266 static ma_uint32 ma_dr_flac__crc32_table[] = { 86267 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 86268 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 86269 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, 86270 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, 86271 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 86272 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 86273 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, 86274 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, 86275 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, 86276 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 86277 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 86278 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, 86279 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, 86280 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, 86281 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 86282 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 86283 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, 86284 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, 86285 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, 86286 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 86287 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 86288 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, 86289 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, 86290 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, 86291 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 86292 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 86293 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, 86294 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, 86295 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, 86296 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 86297 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 86298 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, 86299 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, 86300 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, 86301 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 86302 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, 86303 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, 86304 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, 86305 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, 86306 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 86307 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 86308 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, 86309 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, 86310 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, 86311 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 86312 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 86313 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, 86314 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, 86315 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, 86316 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 86317 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 86318 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, 86319 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, 86320 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, 86321 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 86322 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 86323 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, 86324 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, 86325 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, 86326 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 86327 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 86328 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, 86329 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, 86330 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L 86331 }; 86332 #endif 86333 static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) 86334 { 86335 #ifndef MA_DR_FLAC_NO_CRC 86336 return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; 86337 #else 86338 (void)data; 86339 return crc32; 86340 #endif 86341 } 86342 #if 0 86343 static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) 86344 { 86345 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); 86346 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); 86347 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); 86348 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); 86349 return crc32; 86350 } 86351 static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) 86352 { 86353 crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); 86354 crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); 86355 return crc32; 86356 } 86357 #endif 86358 static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) 86359 { 86360 ma_uint32 i; 86361 for (i = 0; i < dataSize; ++i) { 86362 crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); 86363 } 86364 return crc32; 86365 } 86366 static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) 86367 { 86368 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; 86369 } 86370 static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) 86371 { 86372 return 27 + pHeader->segmentCount; 86373 } 86374 static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) 86375 { 86376 ma_uint32 pageBodySize = 0; 86377 int i; 86378 for (i = 0; i < pHeader->segmentCount; ++i) { 86379 pageBodySize += pHeader->segmentTable[i]; 86380 } 86381 return pageBodySize; 86382 } 86383 static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) 86384 { 86385 ma_uint8 data[23]; 86386 ma_uint32 i; 86387 MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); 86388 if (onRead(pUserData, data, 23) != 23) { 86389 return MA_AT_END; 86390 } 86391 *pBytesRead += 23; 86392 pHeader->capturePattern[0] = 'O'; 86393 pHeader->capturePattern[1] = 'g'; 86394 pHeader->capturePattern[2] = 'g'; 86395 pHeader->capturePattern[3] = 'S'; 86396 pHeader->structureVersion = data[0]; 86397 pHeader->headerType = data[1]; 86398 MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); 86399 MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); 86400 MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); 86401 MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); 86402 pHeader->segmentCount = data[22]; 86403 data[18] = 0; 86404 data[19] = 0; 86405 data[20] = 0; 86406 data[21] = 0; 86407 for (i = 0; i < 23; ++i) { 86408 *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); 86409 } 86410 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { 86411 return MA_AT_END; 86412 } 86413 *pBytesRead += pHeader->segmentCount; 86414 for (i = 0; i < pHeader->segmentCount; ++i) { 86415 *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); 86416 } 86417 return MA_SUCCESS; 86418 } 86419 static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) 86420 { 86421 ma_uint8 id[4]; 86422 *pBytesRead = 0; 86423 if (onRead(pUserData, id, 4) != 4) { 86424 return MA_AT_END; 86425 } 86426 *pBytesRead += 4; 86427 for (;;) { 86428 if (ma_dr_flac_ogg__is_capture_pattern(id)) { 86429 ma_result result; 86430 *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; 86431 result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); 86432 if (result == MA_SUCCESS) { 86433 return MA_SUCCESS; 86434 } else { 86435 if (result == MA_CRC_MISMATCH) { 86436 continue; 86437 } else { 86438 return result; 86439 } 86440 } 86441 } else { 86442 id[0] = id[1]; 86443 id[1] = id[2]; 86444 id[2] = id[3]; 86445 if (onRead(pUserData, &id[3], 1) != 1) { 86446 return MA_AT_END; 86447 } 86448 *pBytesRead += 1; 86449 } 86450 } 86451 } 86452 typedef struct 86453 { 86454 ma_dr_flac_read_proc onRead; 86455 ma_dr_flac_seek_proc onSeek; 86456 void* pUserData; 86457 ma_uint64 currentBytePos; 86458 ma_uint64 firstBytePos; 86459 ma_uint32 serialNumber; 86460 ma_dr_flac_ogg_page_header bosPageHeader; 86461 ma_dr_flac_ogg_page_header currentPageHeader; 86462 ma_uint32 bytesRemainingInPage; 86463 ma_uint32 pageDataSize; 86464 ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; 86465 } ma_dr_flac_oggbs; 86466 static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) 86467 { 86468 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); 86469 oggbs->currentBytePos += bytesActuallyRead; 86470 return bytesActuallyRead; 86471 } 86472 static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) 86473 { 86474 if (origin == ma_dr_flac_seek_origin_start) { 86475 if (offset <= 0x7FFFFFFF) { 86476 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { 86477 return MA_FALSE; 86478 } 86479 oggbs->currentBytePos = offset; 86480 return MA_TRUE; 86481 } else { 86482 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { 86483 return MA_FALSE; 86484 } 86485 oggbs->currentBytePos = offset; 86486 return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); 86487 } 86488 } else { 86489 while (offset > 0x7FFFFFFF) { 86490 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { 86491 return MA_FALSE; 86492 } 86493 oggbs->currentBytePos += 0x7FFFFFFF; 86494 offset -= 0x7FFFFFFF; 86495 } 86496 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { 86497 return MA_FALSE; 86498 } 86499 oggbs->currentBytePos += offset; 86500 return MA_TRUE; 86501 } 86502 } 86503 static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) 86504 { 86505 ma_dr_flac_ogg_page_header header; 86506 for (;;) { 86507 ma_uint32 crc32 = 0; 86508 ma_uint32 bytesRead; 86509 ma_uint32 pageBodySize; 86510 #ifndef MA_DR_FLAC_NO_CRC 86511 ma_uint32 actualCRC32; 86512 #endif 86513 if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { 86514 return MA_FALSE; 86515 } 86516 oggbs->currentBytePos += bytesRead; 86517 pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); 86518 if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { 86519 continue; 86520 } 86521 if (header.serialNumber != oggbs->serialNumber) { 86522 if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { 86523 return MA_FALSE; 86524 } 86525 continue; 86526 } 86527 if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { 86528 return MA_FALSE; 86529 } 86530 oggbs->pageDataSize = pageBodySize; 86531 #ifndef MA_DR_FLAC_NO_CRC 86532 actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); 86533 if (actualCRC32 != header.checksum) { 86534 if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { 86535 continue; 86536 } else { 86537 ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); 86538 return MA_FALSE; 86539 } 86540 } 86541 #else 86542 (void)recoveryMethod; 86543 #endif 86544 oggbs->currentPageHeader = header; 86545 oggbs->bytesRemainingInPage = pageBodySize; 86546 return MA_TRUE; 86547 } 86548 } 86549 #if 0 86550 static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) 86551 { 86552 ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; 86553 ma_uint8 iSeg = 0; 86554 ma_uint32 iByte = 0; 86555 while (iByte < bytesConsumedInPage) { 86556 ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; 86557 if (iByte + segmentSize > bytesConsumedInPage) { 86558 break; 86559 } else { 86560 iSeg += 1; 86561 iByte += segmentSize; 86562 } 86563 } 86564 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); 86565 return iSeg; 86566 } 86567 static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) 86568 { 86569 for (;;) { 86570 ma_bool32 atEndOfPage = MA_FALSE; 86571 ma_uint8 bytesRemainingInSeg; 86572 ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); 86573 ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; 86574 for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { 86575 ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; 86576 if (segmentSize < 255) { 86577 if (iSeg == oggbs->currentPageHeader.segmentCount-1) { 86578 atEndOfPage = MA_TRUE; 86579 } 86580 break; 86581 } 86582 bytesToEndOfPacketOrPage += segmentSize; 86583 } 86584 ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); 86585 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; 86586 if (atEndOfPage) { 86587 if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { 86588 return MA_FALSE; 86589 } 86590 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { 86591 return MA_TRUE; 86592 } 86593 } else { 86594 return MA_TRUE; 86595 } 86596 } 86597 } 86598 static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) 86599 { 86600 return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); 86601 } 86602 #endif 86603 static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) 86604 { 86605 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; 86606 ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; 86607 size_t bytesRead = 0; 86608 MA_DR_FLAC_ASSERT(oggbs != NULL); 86609 MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); 86610 while (bytesRead < bytesToRead) { 86611 size_t bytesRemainingToRead = bytesToRead - bytesRead; 86612 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { 86613 MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); 86614 bytesRead += bytesRemainingToRead; 86615 oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; 86616 break; 86617 } 86618 if (oggbs->bytesRemainingInPage > 0) { 86619 MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); 86620 bytesRead += oggbs->bytesRemainingInPage; 86621 pRunningBufferOut += oggbs->bytesRemainingInPage; 86622 oggbs->bytesRemainingInPage = 0; 86623 } 86624 MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); 86625 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { 86626 break; 86627 } 86628 } 86629 return bytesRead; 86630 } 86631 static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) 86632 { 86633 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; 86634 int bytesSeeked = 0; 86635 MA_DR_FLAC_ASSERT(oggbs != NULL); 86636 MA_DR_FLAC_ASSERT(offset >= 0); 86637 if (origin == ma_dr_flac_seek_origin_start) { 86638 if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { 86639 return MA_FALSE; 86640 } 86641 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { 86642 return MA_FALSE; 86643 } 86644 return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); 86645 } 86646 MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); 86647 while (bytesSeeked < offset) { 86648 int bytesRemainingToSeek = offset - bytesSeeked; 86649 MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); 86650 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { 86651 bytesSeeked += bytesRemainingToSeek; 86652 (void)bytesSeeked; 86653 oggbs->bytesRemainingInPage -= bytesRemainingToSeek; 86654 break; 86655 } 86656 if (oggbs->bytesRemainingInPage > 0) { 86657 bytesSeeked += (int)oggbs->bytesRemainingInPage; 86658 oggbs->bytesRemainingInPage = 0; 86659 } 86660 MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); 86661 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { 86662 return MA_FALSE; 86663 } 86664 } 86665 return MA_TRUE; 86666 } 86667 static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) 86668 { 86669 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; 86670 ma_uint64 originalBytePos; 86671 ma_uint64 runningGranulePosition; 86672 ma_uint64 runningFrameBytePos; 86673 ma_uint64 runningPCMFrameCount; 86674 MA_DR_FLAC_ASSERT(oggbs != NULL); 86675 originalBytePos = oggbs->currentBytePos; 86676 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { 86677 return MA_FALSE; 86678 } 86679 oggbs->bytesRemainingInPage = 0; 86680 runningGranulePosition = 0; 86681 for (;;) { 86682 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { 86683 ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); 86684 return MA_FALSE; 86685 } 86686 runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; 86687 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { 86688 break; 86689 } 86690 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { 86691 if (oggbs->currentPageHeader.segmentTable[0] >= 2) { 86692 ma_uint8 firstBytesInPage[2]; 86693 firstBytesInPage[0] = oggbs->pageData[0]; 86694 firstBytesInPage[1] = oggbs->pageData[1]; 86695 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { 86696 runningGranulePosition = oggbs->currentPageHeader.granulePosition; 86697 } 86698 continue; 86699 } 86700 } 86701 } 86702 if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { 86703 return MA_FALSE; 86704 } 86705 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { 86706 return MA_FALSE; 86707 } 86708 runningPCMFrameCount = runningGranulePosition; 86709 for (;;) { 86710 ma_uint64 firstPCMFrameInFLACFrame = 0; 86711 ma_uint64 lastPCMFrameInFLACFrame = 0; 86712 ma_uint64 pcmFrameCountInThisFrame; 86713 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 86714 return MA_FALSE; 86715 } 86716 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); 86717 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; 86718 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { 86719 ma_result result = ma_dr_flac__decode_flac_frame(pFlac); 86720 if (result == MA_SUCCESS) { 86721 pFlac->currentPCMFrame = pcmFrameIndex; 86722 pFlac->currentFLACFrame.pcmFramesRemaining = 0; 86723 return MA_TRUE; 86724 } else { 86725 return MA_FALSE; 86726 } 86727 } 86728 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { 86729 ma_result result = ma_dr_flac__decode_flac_frame(pFlac); 86730 if (result == MA_SUCCESS) { 86731 ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); 86732 if (pcmFramesToDecode == 0) { 86733 return MA_TRUE; 86734 } 86735 pFlac->currentPCMFrame = runningPCMFrameCount; 86736 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; 86737 } else { 86738 if (result == MA_CRC_MISMATCH) { 86739 continue; 86740 } else { 86741 return MA_FALSE; 86742 } 86743 } 86744 } else { 86745 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); 86746 if (result == MA_SUCCESS) { 86747 runningPCMFrameCount += pcmFrameCountInThisFrame; 86748 } else { 86749 if (result == MA_CRC_MISMATCH) { 86750 continue; 86751 } else { 86752 return MA_FALSE; 86753 } 86754 } 86755 } 86756 } 86757 } 86758 static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) 86759 { 86760 ma_dr_flac_ogg_page_header header; 86761 ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; 86762 ma_uint32 bytesRead = 0; 86763 (void)relaxed; 86764 pInit->container = ma_dr_flac_container_ogg; 86765 pInit->oggFirstBytePos = 0; 86766 if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { 86767 return MA_FALSE; 86768 } 86769 pInit->runningFilePos += bytesRead; 86770 for (;;) { 86771 int pageBodySize; 86772 if ((header.headerType & 0x02) == 0) { 86773 return MA_FALSE; 86774 } 86775 pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); 86776 if (pageBodySize == 51) { 86777 ma_uint32 bytesRemainingInPage = pageBodySize; 86778 ma_uint8 packetType; 86779 if (onRead(pUserData, &packetType, 1) != 1) { 86780 return MA_FALSE; 86781 } 86782 bytesRemainingInPage -= 1; 86783 if (packetType == 0x7F) { 86784 ma_uint8 sig[4]; 86785 if (onRead(pUserData, sig, 4) != 4) { 86786 return MA_FALSE; 86787 } 86788 bytesRemainingInPage -= 4; 86789 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { 86790 ma_uint8 mappingVersion[2]; 86791 if (onRead(pUserData, mappingVersion, 2) != 2) { 86792 return MA_FALSE; 86793 } 86794 if (mappingVersion[0] != 1) { 86795 return MA_FALSE; 86796 } 86797 if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { 86798 return MA_FALSE; 86799 } 86800 if (onRead(pUserData, sig, 4) != 4) { 86801 return MA_FALSE; 86802 } 86803 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { 86804 ma_dr_flac_streaminfo streaminfo; 86805 ma_uint8 isLastBlock; 86806 ma_uint8 blockType; 86807 ma_uint32 blockSize; 86808 if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { 86809 return MA_FALSE; 86810 } 86811 if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { 86812 return MA_FALSE; 86813 } 86814 if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { 86815 pInit->hasStreamInfoBlock = MA_TRUE; 86816 pInit->sampleRate = streaminfo.sampleRate; 86817 pInit->channels = streaminfo.channels; 86818 pInit->bitsPerSample = streaminfo.bitsPerSample; 86819 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; 86820 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; 86821 pInit->hasMetadataBlocks = !isLastBlock; 86822 if (onMeta) { 86823 ma_dr_flac_metadata metadata; 86824 metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; 86825 metadata.pRawData = NULL; 86826 metadata.rawDataSize = 0; 86827 metadata.data.streaminfo = streaminfo; 86828 onMeta(pUserDataMD, &metadata); 86829 } 86830 pInit->runningFilePos += pageBodySize; 86831 pInit->oggFirstBytePos = pInit->runningFilePos - 79; 86832 pInit->oggSerial = header.serialNumber; 86833 pInit->oggBosHeader = header; 86834 break; 86835 } else { 86836 return MA_FALSE; 86837 } 86838 } else { 86839 return MA_FALSE; 86840 } 86841 } else { 86842 if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { 86843 return MA_FALSE; 86844 } 86845 } 86846 } else { 86847 if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { 86848 return MA_FALSE; 86849 } 86850 } 86851 } else { 86852 if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { 86853 return MA_FALSE; 86854 } 86855 } 86856 pInit->runningFilePos += pageBodySize; 86857 if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { 86858 return MA_FALSE; 86859 } 86860 pInit->runningFilePos += bytesRead; 86861 } 86862 pInit->hasMetadataBlocks = MA_TRUE; 86863 return MA_TRUE; 86864 } 86865 #endif 86866 static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) 86867 { 86868 ma_bool32 relaxed; 86869 ma_uint8 id[4]; 86870 if (pInit == NULL || onRead == NULL || onSeek == NULL) { 86871 return MA_FALSE; 86872 } 86873 MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); 86874 pInit->onRead = onRead; 86875 pInit->onSeek = onSeek; 86876 pInit->onMeta = onMeta; 86877 pInit->container = container; 86878 pInit->pUserData = pUserData; 86879 pInit->pUserDataMD = pUserDataMD; 86880 pInit->bs.onRead = onRead; 86881 pInit->bs.onSeek = onSeek; 86882 pInit->bs.pUserData = pUserData; 86883 ma_dr_flac__reset_cache(&pInit->bs); 86884 relaxed = container != ma_dr_flac_container_unknown; 86885 for (;;) { 86886 if (onRead(pUserData, id, 4) != 4) { 86887 return MA_FALSE; 86888 } 86889 pInit->runningFilePos += 4; 86890 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { 86891 ma_uint8 header[6]; 86892 ma_uint8 flags; 86893 ma_uint32 headerSize; 86894 if (onRead(pUserData, header, 6) != 6) { 86895 return MA_FALSE; 86896 } 86897 pInit->runningFilePos += 6; 86898 flags = header[1]; 86899 MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); 86900 headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); 86901 if (flags & 0x10) { 86902 headerSize += 10; 86903 } 86904 if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { 86905 return MA_FALSE; 86906 } 86907 pInit->runningFilePos += headerSize; 86908 } else { 86909 break; 86910 } 86911 } 86912 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { 86913 return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); 86914 } 86915 #ifndef MA_DR_FLAC_NO_OGG 86916 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { 86917 return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); 86918 } 86919 #endif 86920 if (relaxed) { 86921 if (container == ma_dr_flac_container_native) { 86922 return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); 86923 } 86924 #ifndef MA_DR_FLAC_NO_OGG 86925 if (container == ma_dr_flac_container_ogg) { 86926 return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); 86927 } 86928 #endif 86929 } 86930 return MA_FALSE; 86931 } 86932 static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) 86933 { 86934 MA_DR_FLAC_ASSERT(pFlac != NULL); 86935 MA_DR_FLAC_ASSERT(pInit != NULL); 86936 MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); 86937 pFlac->bs = pInit->bs; 86938 pFlac->onMeta = pInit->onMeta; 86939 pFlac->pUserDataMD = pInit->pUserDataMD; 86940 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; 86941 pFlac->sampleRate = pInit->sampleRate; 86942 pFlac->channels = (ma_uint8)pInit->channels; 86943 pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; 86944 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; 86945 pFlac->container = pInit->container; 86946 } 86947 static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) 86948 { 86949 ma_dr_flac_init_info init; 86950 ma_uint32 allocationSize; 86951 ma_uint32 wholeSIMDVectorCountPerChannel; 86952 ma_uint32 decodedSamplesAllocationSize; 86953 #ifndef MA_DR_FLAC_NO_OGG 86954 ma_dr_flac_oggbs* pOggbs = NULL; 86955 #endif 86956 ma_uint64 firstFramePos; 86957 ma_uint64 seektablePos; 86958 ma_uint32 seekpointCount; 86959 ma_allocation_callbacks allocationCallbacks; 86960 ma_dr_flac* pFlac; 86961 ma_dr_flac__init_cpu_caps(); 86962 if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { 86963 return NULL; 86964 } 86965 if (pAllocationCallbacks != NULL) { 86966 allocationCallbacks = *pAllocationCallbacks; 86967 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { 86968 return NULL; 86969 } 86970 } else { 86971 allocationCallbacks.pUserData = NULL; 86972 allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; 86973 allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; 86974 allocationCallbacks.onFree = ma_dr_flac__free_default; 86975 } 86976 allocationSize = sizeof(ma_dr_flac); 86977 if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { 86978 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); 86979 } else { 86980 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; 86981 } 86982 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; 86983 allocationSize += decodedSamplesAllocationSize; 86984 allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; 86985 #ifndef MA_DR_FLAC_NO_OGG 86986 if (init.container == ma_dr_flac_container_ogg) { 86987 allocationSize += sizeof(ma_dr_flac_oggbs); 86988 pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); 86989 if (pOggbs == NULL) { 86990 return NULL; 86991 } 86992 MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); 86993 pOggbs->onRead = onRead; 86994 pOggbs->onSeek = onSeek; 86995 pOggbs->pUserData = pUserData; 86996 pOggbs->currentBytePos = init.oggFirstBytePos; 86997 pOggbs->firstBytePos = init.oggFirstBytePos; 86998 pOggbs->serialNumber = init.oggSerial; 86999 pOggbs->bosPageHeader = init.oggBosHeader; 87000 pOggbs->bytesRemainingInPage = 0; 87001 } 87002 #endif 87003 firstFramePos = 42; 87004 seektablePos = 0; 87005 seekpointCount = 0; 87006 if (init.hasMetadataBlocks) { 87007 ma_dr_flac_read_proc onReadOverride = onRead; 87008 ma_dr_flac_seek_proc onSeekOverride = onSeek; 87009 void* pUserDataOverride = pUserData; 87010 #ifndef MA_DR_FLAC_NO_OGG 87011 if (init.container == ma_dr_flac_container_ogg) { 87012 onReadOverride = ma_dr_flac__on_read_ogg; 87013 onSeekOverride = ma_dr_flac__on_seek_ogg; 87014 pUserDataOverride = (void*)pOggbs; 87015 } 87016 #endif 87017 if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { 87018 #ifndef MA_DR_FLAC_NO_OGG 87019 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); 87020 #endif 87021 return NULL; 87022 } 87023 allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); 87024 } 87025 pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); 87026 if (pFlac == NULL) { 87027 #ifndef MA_DR_FLAC_NO_OGG 87028 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); 87029 #endif 87030 return NULL; 87031 } 87032 ma_dr_flac__init_from_info(pFlac, &init); 87033 pFlac->allocationCallbacks = allocationCallbacks; 87034 pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); 87035 #ifndef MA_DR_FLAC_NO_OGG 87036 if (init.container == ma_dr_flac_container_ogg) { 87037 ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); 87038 MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); 87039 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); 87040 pOggbs = NULL; 87041 pFlac->bs.onRead = ma_dr_flac__on_read_ogg; 87042 pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; 87043 pFlac->bs.pUserData = (void*)pInternalOggbs; 87044 pFlac->_oggbs = (void*)pInternalOggbs; 87045 } 87046 #endif 87047 pFlac->firstFLACFramePosInBytes = firstFramePos; 87048 #ifndef MA_DR_FLAC_NO_OGG 87049 if (init.container == ma_dr_flac_container_ogg) 87050 { 87051 pFlac->pSeekpoints = NULL; 87052 pFlac->seekpointCount = 0; 87053 } 87054 else 87055 #endif 87056 { 87057 if (seektablePos != 0) { 87058 pFlac->seekpointCount = seekpointCount; 87059 pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); 87060 MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); 87061 MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); 87062 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { 87063 ma_uint32 iSeekpoint; 87064 for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { 87065 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { 87066 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); 87067 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); 87068 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); 87069 } else { 87070 pFlac->pSeekpoints = NULL; 87071 pFlac->seekpointCount = 0; 87072 break; 87073 } 87074 } 87075 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { 87076 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); 87077 return NULL; 87078 } 87079 } else { 87080 pFlac->pSeekpoints = NULL; 87081 pFlac->seekpointCount = 0; 87082 } 87083 } 87084 } 87085 if (!init.hasStreamInfoBlock) { 87086 pFlac->currentFLACFrame.header = init.firstFrameHeader; 87087 for (;;) { 87088 ma_result result = ma_dr_flac__decode_flac_frame(pFlac); 87089 if (result == MA_SUCCESS) { 87090 break; 87091 } else { 87092 if (result == MA_CRC_MISMATCH) { 87093 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { 87094 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); 87095 return NULL; 87096 } 87097 continue; 87098 } else { 87099 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); 87100 return NULL; 87101 } 87102 } 87103 } 87104 } 87105 return pFlac; 87106 } 87107 #ifndef MA_DR_FLAC_NO_STDIO 87108 #include <stdio.h> 87109 #ifndef MA_DR_FLAC_NO_WCHAR 87110 #include <wchar.h> 87111 #endif 87112 static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) 87113 { 87114 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); 87115 } 87116 static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) 87117 { 87118 MA_DR_FLAC_ASSERT(offset >= 0); 87119 return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; 87120 } 87121 MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) 87122 { 87123 ma_dr_flac* pFlac; 87124 FILE* pFile; 87125 if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { 87126 return NULL; 87127 } 87128 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 87129 if (pFlac == NULL) { 87130 fclose(pFile); 87131 return NULL; 87132 } 87133 return pFlac; 87134 } 87135 #ifndef MA_DR_FLAC_NO_WCHAR 87136 MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) 87137 { 87138 ma_dr_flac* pFlac; 87139 FILE* pFile; 87140 if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 87141 return NULL; 87142 } 87143 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 87144 if (pFlac == NULL) { 87145 fclose(pFile); 87146 return NULL; 87147 } 87148 return pFlac; 87149 } 87150 #endif 87151 MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87152 { 87153 ma_dr_flac* pFlac; 87154 FILE* pFile; 87155 if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { 87156 return NULL; 87157 } 87158 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); 87159 if (pFlac == NULL) { 87160 fclose(pFile); 87161 return pFlac; 87162 } 87163 return pFlac; 87164 } 87165 #ifndef MA_DR_FLAC_NO_WCHAR 87166 MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87167 { 87168 ma_dr_flac* pFlac; 87169 FILE* pFile; 87170 if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 87171 return NULL; 87172 } 87173 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); 87174 if (pFlac == NULL) { 87175 fclose(pFile); 87176 return pFlac; 87177 } 87178 return pFlac; 87179 } 87180 #endif 87181 #endif 87182 static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) 87183 { 87184 ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; 87185 size_t bytesRemaining; 87186 MA_DR_FLAC_ASSERT(memoryStream != NULL); 87187 MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); 87188 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; 87189 if (bytesToRead > bytesRemaining) { 87190 bytesToRead = bytesRemaining; 87191 } 87192 if (bytesToRead > 0) { 87193 MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); 87194 memoryStream->currentReadPos += bytesToRead; 87195 } 87196 return bytesToRead; 87197 } 87198 static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) 87199 { 87200 ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; 87201 MA_DR_FLAC_ASSERT(memoryStream != NULL); 87202 MA_DR_FLAC_ASSERT(offset >= 0); 87203 if (offset > (ma_int64)memoryStream->dataSize) { 87204 return MA_FALSE; 87205 } 87206 if (origin == ma_dr_flac_seek_origin_current) { 87207 if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { 87208 memoryStream->currentReadPos += offset; 87209 } else { 87210 return MA_FALSE; 87211 } 87212 } else { 87213 if ((ma_uint32)offset <= memoryStream->dataSize) { 87214 memoryStream->currentReadPos = offset; 87215 } else { 87216 return MA_FALSE; 87217 } 87218 } 87219 return MA_TRUE; 87220 } 87221 MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) 87222 { 87223 ma_dr_flac__memory_stream memoryStream; 87224 ma_dr_flac* pFlac; 87225 memoryStream.data = (const ma_uint8*)pData; 87226 memoryStream.dataSize = dataSize; 87227 memoryStream.currentReadPos = 0; 87228 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); 87229 if (pFlac == NULL) { 87230 return NULL; 87231 } 87232 pFlac->memoryStream = memoryStream; 87233 #ifndef MA_DR_FLAC_NO_OGG 87234 if (pFlac->container == ma_dr_flac_container_ogg) 87235 { 87236 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; 87237 oggbs->pUserData = &pFlac->memoryStream; 87238 } 87239 else 87240 #endif 87241 { 87242 pFlac->bs.pUserData = &pFlac->memoryStream; 87243 } 87244 return pFlac; 87245 } 87246 MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87247 { 87248 ma_dr_flac__memory_stream memoryStream; 87249 ma_dr_flac* pFlac; 87250 memoryStream.data = (const ma_uint8*)pData; 87251 memoryStream.dataSize = dataSize; 87252 memoryStream.currentReadPos = 0; 87253 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); 87254 if (pFlac == NULL) { 87255 return NULL; 87256 } 87257 pFlac->memoryStream = memoryStream; 87258 #ifndef MA_DR_FLAC_NO_OGG 87259 if (pFlac->container == ma_dr_flac_container_ogg) 87260 { 87261 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; 87262 oggbs->pUserData = &pFlac->memoryStream; 87263 } 87264 else 87265 #endif 87266 { 87267 pFlac->bs.pUserData = &pFlac->memoryStream; 87268 } 87269 return pFlac; 87270 } 87271 MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87272 { 87273 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); 87274 } 87275 MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87276 { 87277 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); 87278 } 87279 MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87280 { 87281 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); 87282 } 87283 MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 87284 { 87285 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); 87286 } 87287 MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) 87288 { 87289 if (pFlac == NULL) { 87290 return; 87291 } 87292 #ifndef MA_DR_FLAC_NO_STDIO 87293 if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { 87294 fclose((FILE*)pFlac->bs.pUserData); 87295 } 87296 #ifndef MA_DR_FLAC_NO_OGG 87297 if (pFlac->container == ma_dr_flac_container_ogg) { 87298 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; 87299 MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); 87300 if (oggbs->onRead == ma_dr_flac__on_read_stdio) { 87301 fclose((FILE*)oggbs->pUserData); 87302 } 87303 } 87304 #endif 87305 #endif 87306 ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); 87307 } 87308 #if 0 87309 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87310 { 87311 ma_uint64 i; 87312 for (i = 0; i < frameCount; ++i) { 87313 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87314 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87315 ma_uint32 right = left - side; 87316 pOutputSamples[i*2+0] = (ma_int32)left; 87317 pOutputSamples[i*2+1] = (ma_int32)right; 87318 } 87319 } 87320 #endif 87321 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87322 { 87323 ma_uint64 i; 87324 ma_uint64 frameCount4 = frameCount >> 2; 87325 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87326 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87327 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87328 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87329 for (i = 0; i < frameCount4; ++i) { 87330 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; 87331 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; 87332 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; 87333 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; 87334 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; 87335 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; 87336 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; 87337 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; 87338 ma_uint32 right0 = left0 - side0; 87339 ma_uint32 right1 = left1 - side1; 87340 ma_uint32 right2 = left2 - side2; 87341 ma_uint32 right3 = left3 - side3; 87342 pOutputSamples[i*8+0] = (ma_int32)left0; 87343 pOutputSamples[i*8+1] = (ma_int32)right0; 87344 pOutputSamples[i*8+2] = (ma_int32)left1; 87345 pOutputSamples[i*8+3] = (ma_int32)right1; 87346 pOutputSamples[i*8+4] = (ma_int32)left2; 87347 pOutputSamples[i*8+5] = (ma_int32)right2; 87348 pOutputSamples[i*8+6] = (ma_int32)left3; 87349 pOutputSamples[i*8+7] = (ma_int32)right3; 87350 } 87351 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87352 ma_uint32 left = pInputSamples0U32[i] << shift0; 87353 ma_uint32 side = pInputSamples1U32[i] << shift1; 87354 ma_uint32 right = left - side; 87355 pOutputSamples[i*2+0] = (ma_int32)left; 87356 pOutputSamples[i*2+1] = (ma_int32)right; 87357 } 87358 } 87359 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87360 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87361 { 87362 ma_uint64 i; 87363 ma_uint64 frameCount4 = frameCount >> 2; 87364 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87365 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87366 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87367 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87368 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87369 for (i = 0; i < frameCount4; ++i) { 87370 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 87371 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 87372 __m128i right = _mm_sub_epi32(left, side); 87373 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); 87374 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); 87375 } 87376 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87377 ma_uint32 left = pInputSamples0U32[i] << shift0; 87378 ma_uint32 side = pInputSamples1U32[i] << shift1; 87379 ma_uint32 right = left - side; 87380 pOutputSamples[i*2+0] = (ma_int32)left; 87381 pOutputSamples[i*2+1] = (ma_int32)right; 87382 } 87383 } 87384 #endif 87385 #if defined(MA_DR_FLAC_SUPPORT_NEON) 87386 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87387 { 87388 ma_uint64 i; 87389 ma_uint64 frameCount4 = frameCount >> 2; 87390 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87391 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87392 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87393 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87394 int32x4_t shift0_4; 87395 int32x4_t shift1_4; 87396 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87397 shift0_4 = vdupq_n_s32(shift0); 87398 shift1_4 = vdupq_n_s32(shift1); 87399 for (i = 0; i < frameCount4; ++i) { 87400 uint32x4_t left; 87401 uint32x4_t side; 87402 uint32x4_t right; 87403 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 87404 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 87405 right = vsubq_u32(left, side); 87406 ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); 87407 } 87408 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87409 ma_uint32 left = pInputSamples0U32[i] << shift0; 87410 ma_uint32 side = pInputSamples1U32[i] << shift1; 87411 ma_uint32 right = left - side; 87412 pOutputSamples[i*2+0] = (ma_int32)left; 87413 pOutputSamples[i*2+1] = (ma_int32)right; 87414 } 87415 } 87416 #endif 87417 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87418 { 87419 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87420 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 87421 ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87422 } else 87423 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 87424 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 87425 ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87426 } else 87427 #endif 87428 { 87429 #if 0 87430 ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87431 #else 87432 ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87433 #endif 87434 } 87435 } 87436 #if 0 87437 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87438 { 87439 ma_uint64 i; 87440 for (i = 0; i < frameCount; ++i) { 87441 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87442 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87443 ma_uint32 left = right + side; 87444 pOutputSamples[i*2+0] = (ma_int32)left; 87445 pOutputSamples[i*2+1] = (ma_int32)right; 87446 } 87447 } 87448 #endif 87449 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87450 { 87451 ma_uint64 i; 87452 ma_uint64 frameCount4 = frameCount >> 2; 87453 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87454 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87455 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87456 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87457 for (i = 0; i < frameCount4; ++i) { 87458 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; 87459 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; 87460 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; 87461 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; 87462 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; 87463 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; 87464 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; 87465 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; 87466 ma_uint32 left0 = right0 + side0; 87467 ma_uint32 left1 = right1 + side1; 87468 ma_uint32 left2 = right2 + side2; 87469 ma_uint32 left3 = right3 + side3; 87470 pOutputSamples[i*8+0] = (ma_int32)left0; 87471 pOutputSamples[i*8+1] = (ma_int32)right0; 87472 pOutputSamples[i*8+2] = (ma_int32)left1; 87473 pOutputSamples[i*8+3] = (ma_int32)right1; 87474 pOutputSamples[i*8+4] = (ma_int32)left2; 87475 pOutputSamples[i*8+5] = (ma_int32)right2; 87476 pOutputSamples[i*8+6] = (ma_int32)left3; 87477 pOutputSamples[i*8+7] = (ma_int32)right3; 87478 } 87479 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87480 ma_uint32 side = pInputSamples0U32[i] << shift0; 87481 ma_uint32 right = pInputSamples1U32[i] << shift1; 87482 ma_uint32 left = right + side; 87483 pOutputSamples[i*2+0] = (ma_int32)left; 87484 pOutputSamples[i*2+1] = (ma_int32)right; 87485 } 87486 } 87487 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87488 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87489 { 87490 ma_uint64 i; 87491 ma_uint64 frameCount4 = frameCount >> 2; 87492 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87493 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87494 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87495 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87496 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87497 for (i = 0; i < frameCount4; ++i) { 87498 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 87499 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 87500 __m128i left = _mm_add_epi32(right, side); 87501 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); 87502 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); 87503 } 87504 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87505 ma_uint32 side = pInputSamples0U32[i] << shift0; 87506 ma_uint32 right = pInputSamples1U32[i] << shift1; 87507 ma_uint32 left = right + side; 87508 pOutputSamples[i*2+0] = (ma_int32)left; 87509 pOutputSamples[i*2+1] = (ma_int32)right; 87510 } 87511 } 87512 #endif 87513 #if defined(MA_DR_FLAC_SUPPORT_NEON) 87514 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87515 { 87516 ma_uint64 i; 87517 ma_uint64 frameCount4 = frameCount >> 2; 87518 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87519 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87520 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87521 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87522 int32x4_t shift0_4; 87523 int32x4_t shift1_4; 87524 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87525 shift0_4 = vdupq_n_s32(shift0); 87526 shift1_4 = vdupq_n_s32(shift1); 87527 for (i = 0; i < frameCount4; ++i) { 87528 uint32x4_t side; 87529 uint32x4_t right; 87530 uint32x4_t left; 87531 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 87532 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 87533 left = vaddq_u32(right, side); 87534 ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); 87535 } 87536 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87537 ma_uint32 side = pInputSamples0U32[i] << shift0; 87538 ma_uint32 right = pInputSamples1U32[i] << shift1; 87539 ma_uint32 left = right + side; 87540 pOutputSamples[i*2+0] = (ma_int32)left; 87541 pOutputSamples[i*2+1] = (ma_int32)right; 87542 } 87543 } 87544 #endif 87545 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87546 { 87547 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87548 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 87549 ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87550 } else 87551 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 87552 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 87553 ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87554 } else 87555 #endif 87556 { 87557 #if 0 87558 ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87559 #else 87560 ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87561 #endif 87562 } 87563 } 87564 #if 0 87565 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87566 { 87567 for (ma_uint64 i = 0; i < frameCount; ++i) { 87568 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87569 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87570 mid = (mid << 1) | (side & 0x01); 87571 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); 87572 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); 87573 } 87574 } 87575 #endif 87576 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87577 { 87578 ma_uint64 i; 87579 ma_uint64 frameCount4 = frameCount >> 2; 87580 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87581 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87582 ma_int32 shift = unusedBitsPerSample; 87583 if (shift > 0) { 87584 shift -= 1; 87585 for (i = 0; i < frameCount4; ++i) { 87586 ma_uint32 temp0L; 87587 ma_uint32 temp1L; 87588 ma_uint32 temp2L; 87589 ma_uint32 temp3L; 87590 ma_uint32 temp0R; 87591 ma_uint32 temp1R; 87592 ma_uint32 temp2R; 87593 ma_uint32 temp3R; 87594 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87595 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87596 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87597 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87598 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87599 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87600 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87601 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87602 mid0 = (mid0 << 1) | (side0 & 0x01); 87603 mid1 = (mid1 << 1) | (side1 & 0x01); 87604 mid2 = (mid2 << 1) | (side2 & 0x01); 87605 mid3 = (mid3 << 1) | (side3 & 0x01); 87606 temp0L = (mid0 + side0) << shift; 87607 temp1L = (mid1 + side1) << shift; 87608 temp2L = (mid2 + side2) << shift; 87609 temp3L = (mid3 + side3) << shift; 87610 temp0R = (mid0 - side0) << shift; 87611 temp1R = (mid1 - side1) << shift; 87612 temp2R = (mid2 - side2) << shift; 87613 temp3R = (mid3 - side3) << shift; 87614 pOutputSamples[i*8+0] = (ma_int32)temp0L; 87615 pOutputSamples[i*8+1] = (ma_int32)temp0R; 87616 pOutputSamples[i*8+2] = (ma_int32)temp1L; 87617 pOutputSamples[i*8+3] = (ma_int32)temp1R; 87618 pOutputSamples[i*8+4] = (ma_int32)temp2L; 87619 pOutputSamples[i*8+5] = (ma_int32)temp2R; 87620 pOutputSamples[i*8+6] = (ma_int32)temp3L; 87621 pOutputSamples[i*8+7] = (ma_int32)temp3R; 87622 } 87623 } else { 87624 for (i = 0; i < frameCount4; ++i) { 87625 ma_uint32 temp0L; 87626 ma_uint32 temp1L; 87627 ma_uint32 temp2L; 87628 ma_uint32 temp3L; 87629 ma_uint32 temp0R; 87630 ma_uint32 temp1R; 87631 ma_uint32 temp2R; 87632 ma_uint32 temp3R; 87633 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87634 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87635 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87636 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87637 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87638 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87639 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87640 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87641 mid0 = (mid0 << 1) | (side0 & 0x01); 87642 mid1 = (mid1 << 1) | (side1 & 0x01); 87643 mid2 = (mid2 << 1) | (side2 & 0x01); 87644 mid3 = (mid3 << 1) | (side3 & 0x01); 87645 temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); 87646 temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); 87647 temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); 87648 temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); 87649 temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); 87650 temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); 87651 temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); 87652 temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); 87653 pOutputSamples[i*8+0] = (ma_int32)temp0L; 87654 pOutputSamples[i*8+1] = (ma_int32)temp0R; 87655 pOutputSamples[i*8+2] = (ma_int32)temp1L; 87656 pOutputSamples[i*8+3] = (ma_int32)temp1R; 87657 pOutputSamples[i*8+4] = (ma_int32)temp2L; 87658 pOutputSamples[i*8+5] = (ma_int32)temp2R; 87659 pOutputSamples[i*8+6] = (ma_int32)temp3L; 87660 pOutputSamples[i*8+7] = (ma_int32)temp3R; 87661 } 87662 } 87663 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87664 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87665 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87666 mid = (mid << 1) | (side & 0x01); 87667 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); 87668 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); 87669 } 87670 } 87671 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87672 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87673 { 87674 ma_uint64 i; 87675 ma_uint64 frameCount4 = frameCount >> 2; 87676 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87677 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87678 ma_int32 shift = unusedBitsPerSample; 87679 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87680 if (shift == 0) { 87681 for (i = 0; i < frameCount4; ++i) { 87682 __m128i mid; 87683 __m128i side; 87684 __m128i left; 87685 __m128i right; 87686 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87687 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87688 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 87689 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); 87690 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); 87691 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); 87692 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); 87693 } 87694 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87695 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87696 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87697 mid = (mid << 1) | (side & 0x01); 87698 pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; 87699 pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; 87700 } 87701 } else { 87702 shift -= 1; 87703 for (i = 0; i < frameCount4; ++i) { 87704 __m128i mid; 87705 __m128i side; 87706 __m128i left; 87707 __m128i right; 87708 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87709 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87710 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 87711 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); 87712 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); 87713 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); 87714 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); 87715 } 87716 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87717 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87718 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87719 mid = (mid << 1) | (side & 0x01); 87720 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); 87721 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); 87722 } 87723 } 87724 } 87725 #endif 87726 #if defined(MA_DR_FLAC_SUPPORT_NEON) 87727 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87728 { 87729 ma_uint64 i; 87730 ma_uint64 frameCount4 = frameCount >> 2; 87731 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87732 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87733 ma_int32 shift = unusedBitsPerSample; 87734 int32x4_t wbpsShift0_4; 87735 int32x4_t wbpsShift1_4; 87736 uint32x4_t one4; 87737 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 87738 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87739 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87740 one4 = vdupq_n_u32(1); 87741 if (shift == 0) { 87742 for (i = 0; i < frameCount4; ++i) { 87743 uint32x4_t mid; 87744 uint32x4_t side; 87745 int32x4_t left; 87746 int32x4_t right; 87747 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); 87748 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); 87749 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); 87750 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); 87751 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); 87752 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); 87753 } 87754 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87755 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87756 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87757 mid = (mid << 1) | (side & 0x01); 87758 pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; 87759 pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; 87760 } 87761 } else { 87762 int32x4_t shift4; 87763 shift -= 1; 87764 shift4 = vdupq_n_s32(shift); 87765 for (i = 0; i < frameCount4; ++i) { 87766 uint32x4_t mid; 87767 uint32x4_t side; 87768 int32x4_t left; 87769 int32x4_t right; 87770 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); 87771 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); 87772 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); 87773 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); 87774 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); 87775 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); 87776 } 87777 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87778 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87779 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87780 mid = (mid << 1) | (side & 0x01); 87781 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); 87782 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); 87783 } 87784 } 87785 } 87786 #endif 87787 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87788 { 87789 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87790 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 87791 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87792 } else 87793 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 87794 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 87795 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87796 } else 87797 #endif 87798 { 87799 #if 0 87800 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87801 #else 87802 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87803 #endif 87804 } 87805 } 87806 #if 0 87807 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87808 { 87809 for (ma_uint64 i = 0; i < frameCount; ++i) { 87810 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); 87811 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); 87812 } 87813 } 87814 #endif 87815 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87816 { 87817 ma_uint64 i; 87818 ma_uint64 frameCount4 = frameCount >> 2; 87819 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87820 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87821 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87822 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87823 for (i = 0; i < frameCount4; ++i) { 87824 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; 87825 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; 87826 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; 87827 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; 87828 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; 87829 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; 87830 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; 87831 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; 87832 pOutputSamples[i*8+0] = (ma_int32)tempL0; 87833 pOutputSamples[i*8+1] = (ma_int32)tempR0; 87834 pOutputSamples[i*8+2] = (ma_int32)tempL1; 87835 pOutputSamples[i*8+3] = (ma_int32)tempR1; 87836 pOutputSamples[i*8+4] = (ma_int32)tempL2; 87837 pOutputSamples[i*8+5] = (ma_int32)tempR2; 87838 pOutputSamples[i*8+6] = (ma_int32)tempL3; 87839 pOutputSamples[i*8+7] = (ma_int32)tempR3; 87840 } 87841 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87842 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); 87843 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); 87844 } 87845 } 87846 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87847 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87848 { 87849 ma_uint64 i; 87850 ma_uint64 frameCount4 = frameCount >> 2; 87851 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87852 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87853 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87854 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87855 for (i = 0; i < frameCount4; ++i) { 87856 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 87857 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 87858 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); 87859 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); 87860 } 87861 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87862 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); 87863 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); 87864 } 87865 } 87866 #endif 87867 #if defined(MA_DR_FLAC_SUPPORT_NEON) 87868 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87869 { 87870 ma_uint64 i; 87871 ma_uint64 frameCount4 = frameCount >> 2; 87872 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87873 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87874 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87875 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87876 int32x4_t shift4_0 = vdupq_n_s32(shift0); 87877 int32x4_t shift4_1 = vdupq_n_s32(shift1); 87878 for (i = 0; i < frameCount4; ++i) { 87879 int32x4_t left; 87880 int32x4_t right; 87881 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); 87882 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); 87883 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); 87884 } 87885 for (i = (frameCount4 << 2); i < frameCount; ++i) { 87886 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); 87887 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); 87888 } 87889 } 87890 #endif 87891 static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) 87892 { 87893 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 87894 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 87895 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87896 } else 87897 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 87898 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 87899 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87900 } else 87901 #endif 87902 { 87903 #if 0 87904 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87905 #else 87906 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 87907 #endif 87908 } 87909 } 87910 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) 87911 { 87912 ma_uint64 framesRead; 87913 ma_uint32 unusedBitsPerSample; 87914 if (pFlac == NULL || framesToRead == 0) { 87915 return 0; 87916 } 87917 if (pBufferOut == NULL) { 87918 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); 87919 } 87920 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); 87921 unusedBitsPerSample = 32 - pFlac->bitsPerSample; 87922 framesRead = 0; 87923 while (framesToRead > 0) { 87924 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 87925 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { 87926 break; 87927 } 87928 } else { 87929 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); 87930 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; 87931 ma_uint64 frameCountThisIteration = framesToRead; 87932 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { 87933 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; 87934 } 87935 if (channelCount == 2) { 87936 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; 87937 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; 87938 switch (pFlac->currentFLACFrame.header.channelAssignment) 87939 { 87940 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: 87941 { 87942 ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 87943 } break; 87944 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: 87945 { 87946 ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 87947 } break; 87948 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: 87949 { 87950 ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 87951 } break; 87952 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: 87953 default: 87954 { 87955 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 87956 } break; 87957 } 87958 } else { 87959 ma_uint64 i; 87960 for (i = 0; i < frameCountThisIteration; ++i) { 87961 unsigned int j; 87962 for (j = 0; j < channelCount; ++j) { 87963 pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); 87964 } 87965 } 87966 } 87967 framesRead += frameCountThisIteration; 87968 pBufferOut += frameCountThisIteration * channelCount; 87969 framesToRead -= frameCountThisIteration; 87970 pFlac->currentPCMFrame += frameCountThisIteration; 87971 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; 87972 } 87973 } 87974 return framesRead; 87975 } 87976 #if 0 87977 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 87978 { 87979 ma_uint64 i; 87980 for (i = 0; i < frameCount; ++i) { 87981 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 87982 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 87983 ma_uint32 right = left - side; 87984 left >>= 16; 87985 right >>= 16; 87986 pOutputSamples[i*2+0] = (ma_int16)left; 87987 pOutputSamples[i*2+1] = (ma_int16)right; 87988 } 87989 } 87990 #endif 87991 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 87992 { 87993 ma_uint64 i; 87994 ma_uint64 frameCount4 = frameCount >> 2; 87995 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 87996 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 87997 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 87998 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 87999 for (i = 0; i < frameCount4; ++i) { 88000 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; 88001 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; 88002 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; 88003 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; 88004 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; 88005 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; 88006 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; 88007 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; 88008 ma_uint32 right0 = left0 - side0; 88009 ma_uint32 right1 = left1 - side1; 88010 ma_uint32 right2 = left2 - side2; 88011 ma_uint32 right3 = left3 - side3; 88012 left0 >>= 16; 88013 left1 >>= 16; 88014 left2 >>= 16; 88015 left3 >>= 16; 88016 right0 >>= 16; 88017 right1 >>= 16; 88018 right2 >>= 16; 88019 right3 >>= 16; 88020 pOutputSamples[i*8+0] = (ma_int16)left0; 88021 pOutputSamples[i*8+1] = (ma_int16)right0; 88022 pOutputSamples[i*8+2] = (ma_int16)left1; 88023 pOutputSamples[i*8+3] = (ma_int16)right1; 88024 pOutputSamples[i*8+4] = (ma_int16)left2; 88025 pOutputSamples[i*8+5] = (ma_int16)right2; 88026 pOutputSamples[i*8+6] = (ma_int16)left3; 88027 pOutputSamples[i*8+7] = (ma_int16)right3; 88028 } 88029 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88030 ma_uint32 left = pInputSamples0U32[i] << shift0; 88031 ma_uint32 side = pInputSamples1U32[i] << shift1; 88032 ma_uint32 right = left - side; 88033 left >>= 16; 88034 right >>= 16; 88035 pOutputSamples[i*2+0] = (ma_int16)left; 88036 pOutputSamples[i*2+1] = (ma_int16)right; 88037 } 88038 } 88039 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88040 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88041 { 88042 ma_uint64 i; 88043 ma_uint64 frameCount4 = frameCount >> 2; 88044 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88045 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88046 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88047 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88048 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88049 for (i = 0; i < frameCount4; ++i) { 88050 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 88051 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 88052 __m128i right = _mm_sub_epi32(left, side); 88053 left = _mm_srai_epi32(left, 16); 88054 right = _mm_srai_epi32(right, 16); 88055 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); 88056 } 88057 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88058 ma_uint32 left = pInputSamples0U32[i] << shift0; 88059 ma_uint32 side = pInputSamples1U32[i] << shift1; 88060 ma_uint32 right = left - side; 88061 left >>= 16; 88062 right >>= 16; 88063 pOutputSamples[i*2+0] = (ma_int16)left; 88064 pOutputSamples[i*2+1] = (ma_int16)right; 88065 } 88066 } 88067 #endif 88068 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88069 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88070 { 88071 ma_uint64 i; 88072 ma_uint64 frameCount4 = frameCount >> 2; 88073 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88074 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88075 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88076 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88077 int32x4_t shift0_4; 88078 int32x4_t shift1_4; 88079 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88080 shift0_4 = vdupq_n_s32(shift0); 88081 shift1_4 = vdupq_n_s32(shift1); 88082 for (i = 0; i < frameCount4; ++i) { 88083 uint32x4_t left; 88084 uint32x4_t side; 88085 uint32x4_t right; 88086 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 88087 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 88088 right = vsubq_u32(left, side); 88089 left = vshrq_n_u32(left, 16); 88090 right = vshrq_n_u32(right, 16); 88091 ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); 88092 } 88093 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88094 ma_uint32 left = pInputSamples0U32[i] << shift0; 88095 ma_uint32 side = pInputSamples1U32[i] << shift1; 88096 ma_uint32 right = left - side; 88097 left >>= 16; 88098 right >>= 16; 88099 pOutputSamples[i*2+0] = (ma_int16)left; 88100 pOutputSamples[i*2+1] = (ma_int16)right; 88101 } 88102 } 88103 #endif 88104 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88105 { 88106 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88107 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88108 ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88109 } else 88110 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88111 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88112 ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88113 } else 88114 #endif 88115 { 88116 #if 0 88117 ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88118 #else 88119 ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88120 #endif 88121 } 88122 } 88123 #if 0 88124 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88125 { 88126 ma_uint64 i; 88127 for (i = 0; i < frameCount; ++i) { 88128 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88129 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88130 ma_uint32 left = right + side; 88131 left >>= 16; 88132 right >>= 16; 88133 pOutputSamples[i*2+0] = (ma_int16)left; 88134 pOutputSamples[i*2+1] = (ma_int16)right; 88135 } 88136 } 88137 #endif 88138 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88139 { 88140 ma_uint64 i; 88141 ma_uint64 frameCount4 = frameCount >> 2; 88142 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88143 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88144 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88145 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88146 for (i = 0; i < frameCount4; ++i) { 88147 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; 88148 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; 88149 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; 88150 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; 88151 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; 88152 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; 88153 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; 88154 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; 88155 ma_uint32 left0 = right0 + side0; 88156 ma_uint32 left1 = right1 + side1; 88157 ma_uint32 left2 = right2 + side2; 88158 ma_uint32 left3 = right3 + side3; 88159 left0 >>= 16; 88160 left1 >>= 16; 88161 left2 >>= 16; 88162 left3 >>= 16; 88163 right0 >>= 16; 88164 right1 >>= 16; 88165 right2 >>= 16; 88166 right3 >>= 16; 88167 pOutputSamples[i*8+0] = (ma_int16)left0; 88168 pOutputSamples[i*8+1] = (ma_int16)right0; 88169 pOutputSamples[i*8+2] = (ma_int16)left1; 88170 pOutputSamples[i*8+3] = (ma_int16)right1; 88171 pOutputSamples[i*8+4] = (ma_int16)left2; 88172 pOutputSamples[i*8+5] = (ma_int16)right2; 88173 pOutputSamples[i*8+6] = (ma_int16)left3; 88174 pOutputSamples[i*8+7] = (ma_int16)right3; 88175 } 88176 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88177 ma_uint32 side = pInputSamples0U32[i] << shift0; 88178 ma_uint32 right = pInputSamples1U32[i] << shift1; 88179 ma_uint32 left = right + side; 88180 left >>= 16; 88181 right >>= 16; 88182 pOutputSamples[i*2+0] = (ma_int16)left; 88183 pOutputSamples[i*2+1] = (ma_int16)right; 88184 } 88185 } 88186 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88187 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88188 { 88189 ma_uint64 i; 88190 ma_uint64 frameCount4 = frameCount >> 2; 88191 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88192 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88193 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88194 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88195 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88196 for (i = 0; i < frameCount4; ++i) { 88197 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 88198 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 88199 __m128i left = _mm_add_epi32(right, side); 88200 left = _mm_srai_epi32(left, 16); 88201 right = _mm_srai_epi32(right, 16); 88202 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); 88203 } 88204 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88205 ma_uint32 side = pInputSamples0U32[i] << shift0; 88206 ma_uint32 right = pInputSamples1U32[i] << shift1; 88207 ma_uint32 left = right + side; 88208 left >>= 16; 88209 right >>= 16; 88210 pOutputSamples[i*2+0] = (ma_int16)left; 88211 pOutputSamples[i*2+1] = (ma_int16)right; 88212 } 88213 } 88214 #endif 88215 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88216 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88217 { 88218 ma_uint64 i; 88219 ma_uint64 frameCount4 = frameCount >> 2; 88220 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88221 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88222 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88223 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88224 int32x4_t shift0_4; 88225 int32x4_t shift1_4; 88226 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88227 shift0_4 = vdupq_n_s32(shift0); 88228 shift1_4 = vdupq_n_s32(shift1); 88229 for (i = 0; i < frameCount4; ++i) { 88230 uint32x4_t side; 88231 uint32x4_t right; 88232 uint32x4_t left; 88233 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 88234 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 88235 left = vaddq_u32(right, side); 88236 left = vshrq_n_u32(left, 16); 88237 right = vshrq_n_u32(right, 16); 88238 ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); 88239 } 88240 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88241 ma_uint32 side = pInputSamples0U32[i] << shift0; 88242 ma_uint32 right = pInputSamples1U32[i] << shift1; 88243 ma_uint32 left = right + side; 88244 left >>= 16; 88245 right >>= 16; 88246 pOutputSamples[i*2+0] = (ma_int16)left; 88247 pOutputSamples[i*2+1] = (ma_int16)right; 88248 } 88249 } 88250 #endif 88251 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88252 { 88253 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88254 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88255 ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88256 } else 88257 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88258 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88259 ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88260 } else 88261 #endif 88262 { 88263 #if 0 88264 ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88265 #else 88266 ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88267 #endif 88268 } 88269 } 88270 #if 0 88271 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88272 { 88273 for (ma_uint64 i = 0; i < frameCount; ++i) { 88274 ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88275 ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88276 mid = (mid << 1) | (side & 0x01); 88277 pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); 88278 pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); 88279 } 88280 } 88281 #endif 88282 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88283 { 88284 ma_uint64 i; 88285 ma_uint64 frameCount4 = frameCount >> 2; 88286 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88287 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88288 ma_uint32 shift = unusedBitsPerSample; 88289 if (shift > 0) { 88290 shift -= 1; 88291 for (i = 0; i < frameCount4; ++i) { 88292 ma_uint32 temp0L; 88293 ma_uint32 temp1L; 88294 ma_uint32 temp2L; 88295 ma_uint32 temp3L; 88296 ma_uint32 temp0R; 88297 ma_uint32 temp1R; 88298 ma_uint32 temp2R; 88299 ma_uint32 temp3R; 88300 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88301 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88302 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88303 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88304 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88305 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88306 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88307 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88308 mid0 = (mid0 << 1) | (side0 & 0x01); 88309 mid1 = (mid1 << 1) | (side1 & 0x01); 88310 mid2 = (mid2 << 1) | (side2 & 0x01); 88311 mid3 = (mid3 << 1) | (side3 & 0x01); 88312 temp0L = (mid0 + side0) << shift; 88313 temp1L = (mid1 + side1) << shift; 88314 temp2L = (mid2 + side2) << shift; 88315 temp3L = (mid3 + side3) << shift; 88316 temp0R = (mid0 - side0) << shift; 88317 temp1R = (mid1 - side1) << shift; 88318 temp2R = (mid2 - side2) << shift; 88319 temp3R = (mid3 - side3) << shift; 88320 temp0L >>= 16; 88321 temp1L >>= 16; 88322 temp2L >>= 16; 88323 temp3L >>= 16; 88324 temp0R >>= 16; 88325 temp1R >>= 16; 88326 temp2R >>= 16; 88327 temp3R >>= 16; 88328 pOutputSamples[i*8+0] = (ma_int16)temp0L; 88329 pOutputSamples[i*8+1] = (ma_int16)temp0R; 88330 pOutputSamples[i*8+2] = (ma_int16)temp1L; 88331 pOutputSamples[i*8+3] = (ma_int16)temp1R; 88332 pOutputSamples[i*8+4] = (ma_int16)temp2L; 88333 pOutputSamples[i*8+5] = (ma_int16)temp2R; 88334 pOutputSamples[i*8+6] = (ma_int16)temp3L; 88335 pOutputSamples[i*8+7] = (ma_int16)temp3R; 88336 } 88337 } else { 88338 for (i = 0; i < frameCount4; ++i) { 88339 ma_uint32 temp0L; 88340 ma_uint32 temp1L; 88341 ma_uint32 temp2L; 88342 ma_uint32 temp3L; 88343 ma_uint32 temp0R; 88344 ma_uint32 temp1R; 88345 ma_uint32 temp2R; 88346 ma_uint32 temp3R; 88347 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88348 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88349 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88350 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88351 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88352 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88353 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88354 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88355 mid0 = (mid0 << 1) | (side0 & 0x01); 88356 mid1 = (mid1 << 1) | (side1 & 0x01); 88357 mid2 = (mid2 << 1) | (side2 & 0x01); 88358 mid3 = (mid3 << 1) | (side3 & 0x01); 88359 temp0L = ((ma_int32)(mid0 + side0) >> 1); 88360 temp1L = ((ma_int32)(mid1 + side1) >> 1); 88361 temp2L = ((ma_int32)(mid2 + side2) >> 1); 88362 temp3L = ((ma_int32)(mid3 + side3) >> 1); 88363 temp0R = ((ma_int32)(mid0 - side0) >> 1); 88364 temp1R = ((ma_int32)(mid1 - side1) >> 1); 88365 temp2R = ((ma_int32)(mid2 - side2) >> 1); 88366 temp3R = ((ma_int32)(mid3 - side3) >> 1); 88367 temp0L >>= 16; 88368 temp1L >>= 16; 88369 temp2L >>= 16; 88370 temp3L >>= 16; 88371 temp0R >>= 16; 88372 temp1R >>= 16; 88373 temp2R >>= 16; 88374 temp3R >>= 16; 88375 pOutputSamples[i*8+0] = (ma_int16)temp0L; 88376 pOutputSamples[i*8+1] = (ma_int16)temp0R; 88377 pOutputSamples[i*8+2] = (ma_int16)temp1L; 88378 pOutputSamples[i*8+3] = (ma_int16)temp1R; 88379 pOutputSamples[i*8+4] = (ma_int16)temp2L; 88380 pOutputSamples[i*8+5] = (ma_int16)temp2R; 88381 pOutputSamples[i*8+6] = (ma_int16)temp3L; 88382 pOutputSamples[i*8+7] = (ma_int16)temp3R; 88383 } 88384 } 88385 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88386 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88387 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88388 mid = (mid << 1) | (side & 0x01); 88389 pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); 88390 pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); 88391 } 88392 } 88393 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88394 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88395 { 88396 ma_uint64 i; 88397 ma_uint64 frameCount4 = frameCount >> 2; 88398 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88399 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88400 ma_uint32 shift = unusedBitsPerSample; 88401 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88402 if (shift == 0) { 88403 for (i = 0; i < frameCount4; ++i) { 88404 __m128i mid; 88405 __m128i side; 88406 __m128i left; 88407 __m128i right; 88408 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88409 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88410 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 88411 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); 88412 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); 88413 left = _mm_srai_epi32(left, 16); 88414 right = _mm_srai_epi32(right, 16); 88415 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); 88416 } 88417 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88418 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88419 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88420 mid = (mid << 1) | (side & 0x01); 88421 pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); 88422 pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); 88423 } 88424 } else { 88425 shift -= 1; 88426 for (i = 0; i < frameCount4; ++i) { 88427 __m128i mid; 88428 __m128i side; 88429 __m128i left; 88430 __m128i right; 88431 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88432 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88433 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 88434 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); 88435 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); 88436 left = _mm_srai_epi32(left, 16); 88437 right = _mm_srai_epi32(right, 16); 88438 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); 88439 } 88440 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88441 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88442 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88443 mid = (mid << 1) | (side & 0x01); 88444 pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); 88445 pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); 88446 } 88447 } 88448 } 88449 #endif 88450 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88451 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88452 { 88453 ma_uint64 i; 88454 ma_uint64 frameCount4 = frameCount >> 2; 88455 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88456 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88457 ma_uint32 shift = unusedBitsPerSample; 88458 int32x4_t wbpsShift0_4; 88459 int32x4_t wbpsShift1_4; 88460 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88461 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88462 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88463 if (shift == 0) { 88464 for (i = 0; i < frameCount4; ++i) { 88465 uint32x4_t mid; 88466 uint32x4_t side; 88467 int32x4_t left; 88468 int32x4_t right; 88469 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); 88470 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); 88471 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); 88472 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); 88473 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); 88474 left = vshrq_n_s32(left, 16); 88475 right = vshrq_n_s32(right, 16); 88476 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); 88477 } 88478 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88479 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88480 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88481 mid = (mid << 1) | (side & 0x01); 88482 pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); 88483 pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); 88484 } 88485 } else { 88486 int32x4_t shift4; 88487 shift -= 1; 88488 shift4 = vdupq_n_s32(shift); 88489 for (i = 0; i < frameCount4; ++i) { 88490 uint32x4_t mid; 88491 uint32x4_t side; 88492 int32x4_t left; 88493 int32x4_t right; 88494 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); 88495 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); 88496 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); 88497 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); 88498 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); 88499 left = vshrq_n_s32(left, 16); 88500 right = vshrq_n_s32(right, 16); 88501 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); 88502 } 88503 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88504 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88505 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88506 mid = (mid << 1) | (side & 0x01); 88507 pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); 88508 pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); 88509 } 88510 } 88511 } 88512 #endif 88513 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88514 { 88515 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88516 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88517 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88518 } else 88519 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88520 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88521 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88522 } else 88523 #endif 88524 { 88525 #if 0 88526 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88527 #else 88528 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88529 #endif 88530 } 88531 } 88532 #if 0 88533 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88534 { 88535 for (ma_uint64 i = 0; i < frameCount; ++i) { 88536 pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); 88537 pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); 88538 } 88539 } 88540 #endif 88541 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88542 { 88543 ma_uint64 i; 88544 ma_uint64 frameCount4 = frameCount >> 2; 88545 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88546 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88547 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88548 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88549 for (i = 0; i < frameCount4; ++i) { 88550 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; 88551 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; 88552 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; 88553 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; 88554 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; 88555 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; 88556 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; 88557 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; 88558 tempL0 >>= 16; 88559 tempL1 >>= 16; 88560 tempL2 >>= 16; 88561 tempL3 >>= 16; 88562 tempR0 >>= 16; 88563 tempR1 >>= 16; 88564 tempR2 >>= 16; 88565 tempR3 >>= 16; 88566 pOutputSamples[i*8+0] = (ma_int16)tempL0; 88567 pOutputSamples[i*8+1] = (ma_int16)tempR0; 88568 pOutputSamples[i*8+2] = (ma_int16)tempL1; 88569 pOutputSamples[i*8+3] = (ma_int16)tempR1; 88570 pOutputSamples[i*8+4] = (ma_int16)tempL2; 88571 pOutputSamples[i*8+5] = (ma_int16)tempR2; 88572 pOutputSamples[i*8+6] = (ma_int16)tempL3; 88573 pOutputSamples[i*8+7] = (ma_int16)tempR3; 88574 } 88575 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88576 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); 88577 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); 88578 } 88579 } 88580 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88581 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88582 { 88583 ma_uint64 i; 88584 ma_uint64 frameCount4 = frameCount >> 2; 88585 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88586 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88587 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88588 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88589 for (i = 0; i < frameCount4; ++i) { 88590 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 88591 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 88592 left = _mm_srai_epi32(left, 16); 88593 right = _mm_srai_epi32(right, 16); 88594 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); 88595 } 88596 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88597 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); 88598 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); 88599 } 88600 } 88601 #endif 88602 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88603 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88604 { 88605 ma_uint64 i; 88606 ma_uint64 frameCount4 = frameCount >> 2; 88607 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88608 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88609 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88610 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88611 int32x4_t shift0_4 = vdupq_n_s32(shift0); 88612 int32x4_t shift1_4 = vdupq_n_s32(shift1); 88613 for (i = 0; i < frameCount4; ++i) { 88614 int32x4_t left; 88615 int32x4_t right; 88616 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); 88617 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); 88618 left = vshrq_n_s32(left, 16); 88619 right = vshrq_n_s32(right, 16); 88620 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); 88621 } 88622 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88623 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); 88624 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); 88625 } 88626 } 88627 #endif 88628 static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) 88629 { 88630 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88631 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88632 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88633 } else 88634 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88635 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88636 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88637 } else 88638 #endif 88639 { 88640 #if 0 88641 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88642 #else 88643 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88644 #endif 88645 } 88646 } 88647 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) 88648 { 88649 ma_uint64 framesRead; 88650 ma_uint32 unusedBitsPerSample; 88651 if (pFlac == NULL || framesToRead == 0) { 88652 return 0; 88653 } 88654 if (pBufferOut == NULL) { 88655 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); 88656 } 88657 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); 88658 unusedBitsPerSample = 32 - pFlac->bitsPerSample; 88659 framesRead = 0; 88660 while (framesToRead > 0) { 88661 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 88662 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { 88663 break; 88664 } 88665 } else { 88666 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); 88667 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; 88668 ma_uint64 frameCountThisIteration = framesToRead; 88669 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { 88670 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; 88671 } 88672 if (channelCount == 2) { 88673 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; 88674 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; 88675 switch (pFlac->currentFLACFrame.header.channelAssignment) 88676 { 88677 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: 88678 { 88679 ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 88680 } break; 88681 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: 88682 { 88683 ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 88684 } break; 88685 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: 88686 { 88687 ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 88688 } break; 88689 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: 88690 default: 88691 { 88692 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 88693 } break; 88694 } 88695 } else { 88696 ma_uint64 i; 88697 for (i = 0; i < frameCountThisIteration; ++i) { 88698 unsigned int j; 88699 for (j = 0; j < channelCount; ++j) { 88700 ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); 88701 pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); 88702 } 88703 } 88704 } 88705 framesRead += frameCountThisIteration; 88706 pBufferOut += frameCountThisIteration * channelCount; 88707 framesToRead -= frameCountThisIteration; 88708 pFlac->currentPCMFrame += frameCountThisIteration; 88709 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; 88710 } 88711 } 88712 return framesRead; 88713 } 88714 #if 0 88715 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88716 { 88717 ma_uint64 i; 88718 for (i = 0; i < frameCount; ++i) { 88719 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88720 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88721 ma_uint32 right = left - side; 88722 pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); 88723 pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); 88724 } 88725 } 88726 #endif 88727 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88728 { 88729 ma_uint64 i; 88730 ma_uint64 frameCount4 = frameCount >> 2; 88731 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88732 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88733 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88734 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88735 float factor = 1 / 2147483648.0; 88736 for (i = 0; i < frameCount4; ++i) { 88737 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; 88738 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; 88739 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; 88740 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; 88741 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; 88742 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; 88743 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; 88744 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; 88745 ma_uint32 right0 = left0 - side0; 88746 ma_uint32 right1 = left1 - side1; 88747 ma_uint32 right2 = left2 - side2; 88748 ma_uint32 right3 = left3 - side3; 88749 pOutputSamples[i*8+0] = (ma_int32)left0 * factor; 88750 pOutputSamples[i*8+1] = (ma_int32)right0 * factor; 88751 pOutputSamples[i*8+2] = (ma_int32)left1 * factor; 88752 pOutputSamples[i*8+3] = (ma_int32)right1 * factor; 88753 pOutputSamples[i*8+4] = (ma_int32)left2 * factor; 88754 pOutputSamples[i*8+5] = (ma_int32)right2 * factor; 88755 pOutputSamples[i*8+6] = (ma_int32)left3 * factor; 88756 pOutputSamples[i*8+7] = (ma_int32)right3 * factor; 88757 } 88758 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88759 ma_uint32 left = pInputSamples0U32[i] << shift0; 88760 ma_uint32 side = pInputSamples1U32[i] << shift1; 88761 ma_uint32 right = left - side; 88762 pOutputSamples[i*2+0] = (ma_int32)left * factor; 88763 pOutputSamples[i*2+1] = (ma_int32)right * factor; 88764 } 88765 } 88766 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88767 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88768 { 88769 ma_uint64 i; 88770 ma_uint64 frameCount4 = frameCount >> 2; 88771 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88772 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88773 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 88774 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 88775 __m128 factor; 88776 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88777 factor = _mm_set1_ps(1.0f / 8388608.0f); 88778 for (i = 0; i < frameCount4; ++i) { 88779 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 88780 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 88781 __m128i right = _mm_sub_epi32(left, side); 88782 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); 88783 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); 88784 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); 88785 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); 88786 } 88787 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88788 ma_uint32 left = pInputSamples0U32[i] << shift0; 88789 ma_uint32 side = pInputSamples1U32[i] << shift1; 88790 ma_uint32 right = left - side; 88791 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; 88792 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; 88793 } 88794 } 88795 #endif 88796 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88797 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88798 { 88799 ma_uint64 i; 88800 ma_uint64 frameCount4 = frameCount >> 2; 88801 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88802 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88803 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 88804 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 88805 float32x4_t factor4; 88806 int32x4_t shift0_4; 88807 int32x4_t shift1_4; 88808 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88809 factor4 = vdupq_n_f32(1.0f / 8388608.0f); 88810 shift0_4 = vdupq_n_s32(shift0); 88811 shift1_4 = vdupq_n_s32(shift1); 88812 for (i = 0; i < frameCount4; ++i) { 88813 uint32x4_t left; 88814 uint32x4_t side; 88815 uint32x4_t right; 88816 float32x4_t leftf; 88817 float32x4_t rightf; 88818 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 88819 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 88820 right = vsubq_u32(left, side); 88821 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); 88822 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); 88823 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); 88824 } 88825 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88826 ma_uint32 left = pInputSamples0U32[i] << shift0; 88827 ma_uint32 side = pInputSamples1U32[i] << shift1; 88828 ma_uint32 right = left - side; 88829 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; 88830 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; 88831 } 88832 } 88833 #endif 88834 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88835 { 88836 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88837 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88838 ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88839 } else 88840 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88841 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88842 ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88843 } else 88844 #endif 88845 { 88846 #if 0 88847 ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88848 #else 88849 ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88850 #endif 88851 } 88852 } 88853 #if 0 88854 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88855 { 88856 ma_uint64 i; 88857 for (i = 0; i < frameCount; ++i) { 88858 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 88859 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 88860 ma_uint32 left = right + side; 88861 pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); 88862 pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); 88863 } 88864 } 88865 #endif 88866 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88867 { 88868 ma_uint64 i; 88869 ma_uint64 frameCount4 = frameCount >> 2; 88870 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88871 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88872 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88873 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88874 float factor = 1 / 2147483648.0; 88875 for (i = 0; i < frameCount4; ++i) { 88876 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; 88877 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; 88878 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; 88879 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; 88880 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; 88881 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; 88882 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; 88883 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; 88884 ma_uint32 left0 = right0 + side0; 88885 ma_uint32 left1 = right1 + side1; 88886 ma_uint32 left2 = right2 + side2; 88887 ma_uint32 left3 = right3 + side3; 88888 pOutputSamples[i*8+0] = (ma_int32)left0 * factor; 88889 pOutputSamples[i*8+1] = (ma_int32)right0 * factor; 88890 pOutputSamples[i*8+2] = (ma_int32)left1 * factor; 88891 pOutputSamples[i*8+3] = (ma_int32)right1 * factor; 88892 pOutputSamples[i*8+4] = (ma_int32)left2 * factor; 88893 pOutputSamples[i*8+5] = (ma_int32)right2 * factor; 88894 pOutputSamples[i*8+6] = (ma_int32)left3 * factor; 88895 pOutputSamples[i*8+7] = (ma_int32)right3 * factor; 88896 } 88897 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88898 ma_uint32 side = pInputSamples0U32[i] << shift0; 88899 ma_uint32 right = pInputSamples1U32[i] << shift1; 88900 ma_uint32 left = right + side; 88901 pOutputSamples[i*2+0] = (ma_int32)left * factor; 88902 pOutputSamples[i*2+1] = (ma_int32)right * factor; 88903 } 88904 } 88905 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88906 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88907 { 88908 ma_uint64 i; 88909 ma_uint64 frameCount4 = frameCount >> 2; 88910 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88911 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88912 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 88913 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 88914 __m128 factor; 88915 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88916 factor = _mm_set1_ps(1.0f / 8388608.0f); 88917 for (i = 0; i < frameCount4; ++i) { 88918 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 88919 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 88920 __m128i left = _mm_add_epi32(right, side); 88921 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); 88922 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); 88923 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); 88924 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); 88925 } 88926 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88927 ma_uint32 side = pInputSamples0U32[i] << shift0; 88928 ma_uint32 right = pInputSamples1U32[i] << shift1; 88929 ma_uint32 left = right + side; 88930 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; 88931 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; 88932 } 88933 } 88934 #endif 88935 #if defined(MA_DR_FLAC_SUPPORT_NEON) 88936 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88937 { 88938 ma_uint64 i; 88939 ma_uint64 frameCount4 = frameCount >> 2; 88940 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 88941 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 88942 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 88943 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 88944 float32x4_t factor4; 88945 int32x4_t shift0_4; 88946 int32x4_t shift1_4; 88947 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 88948 factor4 = vdupq_n_f32(1.0f / 8388608.0f); 88949 shift0_4 = vdupq_n_s32(shift0); 88950 shift1_4 = vdupq_n_s32(shift1); 88951 for (i = 0; i < frameCount4; ++i) { 88952 uint32x4_t side; 88953 uint32x4_t right; 88954 uint32x4_t left; 88955 float32x4_t leftf; 88956 float32x4_t rightf; 88957 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); 88958 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); 88959 left = vaddq_u32(right, side); 88960 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); 88961 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); 88962 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); 88963 } 88964 for (i = (frameCount4 << 2); i < frameCount; ++i) { 88965 ma_uint32 side = pInputSamples0U32[i] << shift0; 88966 ma_uint32 right = pInputSamples1U32[i] << shift1; 88967 ma_uint32 left = right + side; 88968 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; 88969 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; 88970 } 88971 } 88972 #endif 88973 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88974 { 88975 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 88976 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 88977 ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88978 } else 88979 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 88980 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 88981 ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88982 } else 88983 #endif 88984 { 88985 #if 0 88986 ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88987 #else 88988 ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 88989 #endif 88990 } 88991 } 88992 #if 0 88993 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 88994 { 88995 for (ma_uint64 i = 0; i < frameCount; ++i) { 88996 ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 88997 ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 88998 mid = (mid << 1) | (side & 0x01); 88999 pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); 89000 pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); 89001 } 89002 } 89003 #endif 89004 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89005 { 89006 ma_uint64 i; 89007 ma_uint64 frameCount4 = frameCount >> 2; 89008 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89009 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89010 ma_uint32 shift = unusedBitsPerSample; 89011 float factor = 1 / 2147483648.0; 89012 if (shift > 0) { 89013 shift -= 1; 89014 for (i = 0; i < frameCount4; ++i) { 89015 ma_uint32 temp0L; 89016 ma_uint32 temp1L; 89017 ma_uint32 temp2L; 89018 ma_uint32 temp3L; 89019 ma_uint32 temp0R; 89020 ma_uint32 temp1R; 89021 ma_uint32 temp2R; 89022 ma_uint32 temp3R; 89023 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89024 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89025 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89026 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89027 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89028 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89029 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89030 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89031 mid0 = (mid0 << 1) | (side0 & 0x01); 89032 mid1 = (mid1 << 1) | (side1 & 0x01); 89033 mid2 = (mid2 << 1) | (side2 & 0x01); 89034 mid3 = (mid3 << 1) | (side3 & 0x01); 89035 temp0L = (mid0 + side0) << shift; 89036 temp1L = (mid1 + side1) << shift; 89037 temp2L = (mid2 + side2) << shift; 89038 temp3L = (mid3 + side3) << shift; 89039 temp0R = (mid0 - side0) << shift; 89040 temp1R = (mid1 - side1) << shift; 89041 temp2R = (mid2 - side2) << shift; 89042 temp3R = (mid3 - side3) << shift; 89043 pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; 89044 pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; 89045 pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; 89046 pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; 89047 pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; 89048 pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; 89049 pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; 89050 pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; 89051 } 89052 } else { 89053 for (i = 0; i < frameCount4; ++i) { 89054 ma_uint32 temp0L; 89055 ma_uint32 temp1L; 89056 ma_uint32 temp2L; 89057 ma_uint32 temp3L; 89058 ma_uint32 temp0R; 89059 ma_uint32 temp1R; 89060 ma_uint32 temp2R; 89061 ma_uint32 temp3R; 89062 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89063 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89064 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89065 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89066 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89067 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89068 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89069 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89070 mid0 = (mid0 << 1) | (side0 & 0x01); 89071 mid1 = (mid1 << 1) | (side1 & 0x01); 89072 mid2 = (mid2 << 1) | (side2 & 0x01); 89073 mid3 = (mid3 << 1) | (side3 & 0x01); 89074 temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); 89075 temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); 89076 temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); 89077 temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); 89078 temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); 89079 temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); 89080 temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); 89081 temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); 89082 pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; 89083 pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; 89084 pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; 89085 pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; 89086 pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; 89087 pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; 89088 pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; 89089 pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; 89090 } 89091 } 89092 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89093 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89094 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89095 mid = (mid << 1) | (side & 0x01); 89096 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; 89097 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; 89098 } 89099 } 89100 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 89101 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89102 { 89103 ma_uint64 i; 89104 ma_uint64 frameCount4 = frameCount >> 2; 89105 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89106 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89107 ma_uint32 shift = unusedBitsPerSample - 8; 89108 float factor; 89109 __m128 factor128; 89110 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 89111 factor = 1.0f / 8388608.0f; 89112 factor128 = _mm_set1_ps(factor); 89113 if (shift == 0) { 89114 for (i = 0; i < frameCount4; ++i) { 89115 __m128i mid; 89116 __m128i side; 89117 __m128i tempL; 89118 __m128i tempR; 89119 __m128 leftf; 89120 __m128 rightf; 89121 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 89122 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 89123 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 89124 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); 89125 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); 89126 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); 89127 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); 89128 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); 89129 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); 89130 } 89131 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89132 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89133 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89134 mid = (mid << 1) | (side & 0x01); 89135 pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; 89136 pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; 89137 } 89138 } else { 89139 shift -= 1; 89140 for (i = 0; i < frameCount4; ++i) { 89141 __m128i mid; 89142 __m128i side; 89143 __m128i tempL; 89144 __m128i tempR; 89145 __m128 leftf; 89146 __m128 rightf; 89147 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 89148 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 89149 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); 89150 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); 89151 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); 89152 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); 89153 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); 89154 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); 89155 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); 89156 } 89157 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89158 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89159 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89160 mid = (mid << 1) | (side & 0x01); 89161 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; 89162 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; 89163 } 89164 } 89165 } 89166 #endif 89167 #if defined(MA_DR_FLAC_SUPPORT_NEON) 89168 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89169 { 89170 ma_uint64 i; 89171 ma_uint64 frameCount4 = frameCount >> 2; 89172 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89173 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89174 ma_uint32 shift = unusedBitsPerSample - 8; 89175 float factor; 89176 float32x4_t factor4; 89177 int32x4_t shift4; 89178 int32x4_t wbps0_4; 89179 int32x4_t wbps1_4; 89180 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); 89181 factor = 1.0f / 8388608.0f; 89182 factor4 = vdupq_n_f32(factor); 89183 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); 89184 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); 89185 if (shift == 0) { 89186 for (i = 0; i < frameCount4; ++i) { 89187 int32x4_t lefti; 89188 int32x4_t righti; 89189 float32x4_t leftf; 89190 float32x4_t rightf; 89191 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); 89192 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); 89193 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); 89194 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); 89195 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); 89196 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); 89197 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); 89198 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); 89199 } 89200 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89201 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89202 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89203 mid = (mid << 1) | (side & 0x01); 89204 pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; 89205 pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; 89206 } 89207 } else { 89208 shift -= 1; 89209 shift4 = vdupq_n_s32(shift); 89210 for (i = 0; i < frameCount4; ++i) { 89211 uint32x4_t mid; 89212 uint32x4_t side; 89213 int32x4_t lefti; 89214 int32x4_t righti; 89215 float32x4_t leftf; 89216 float32x4_t rightf; 89217 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); 89218 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); 89219 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); 89220 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); 89221 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); 89222 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); 89223 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); 89224 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); 89225 } 89226 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89227 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89228 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89229 mid = (mid << 1) | (side & 0x01); 89230 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; 89231 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; 89232 } 89233 } 89234 } 89235 #endif 89236 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89237 { 89238 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 89239 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 89240 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89241 } else 89242 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 89243 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 89244 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89245 } else 89246 #endif 89247 { 89248 #if 0 89249 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89250 #else 89251 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89252 #endif 89253 } 89254 } 89255 #if 0 89256 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89257 { 89258 for (ma_uint64 i = 0; i < frameCount; ++i) { 89259 pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); 89260 pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); 89261 } 89262 } 89263 #endif 89264 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89265 { 89266 ma_uint64 i; 89267 ma_uint64 frameCount4 = frameCount >> 2; 89268 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89269 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89270 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; 89271 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; 89272 float factor = 1 / 2147483648.0; 89273 for (i = 0; i < frameCount4; ++i) { 89274 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; 89275 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; 89276 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; 89277 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; 89278 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; 89279 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; 89280 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; 89281 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; 89282 pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; 89283 pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; 89284 pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; 89285 pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; 89286 pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; 89287 pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; 89288 pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; 89289 pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; 89290 } 89291 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89292 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; 89293 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; 89294 } 89295 } 89296 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 89297 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89298 { 89299 ma_uint64 i; 89300 ma_uint64 frameCount4 = frameCount >> 2; 89301 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89302 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89303 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 89304 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 89305 float factor = 1.0f / 8388608.0f; 89306 __m128 factor128 = _mm_set1_ps(factor); 89307 for (i = 0; i < frameCount4; ++i) { 89308 __m128i lefti; 89309 __m128i righti; 89310 __m128 leftf; 89311 __m128 rightf; 89312 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); 89313 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); 89314 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); 89315 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); 89316 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); 89317 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); 89318 } 89319 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89320 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; 89321 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; 89322 } 89323 } 89324 #endif 89325 #if defined(MA_DR_FLAC_SUPPORT_NEON) 89326 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89327 { 89328 ma_uint64 i; 89329 ma_uint64 frameCount4 = frameCount >> 2; 89330 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; 89331 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; 89332 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; 89333 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; 89334 float factor = 1.0f / 8388608.0f; 89335 float32x4_t factor4 = vdupq_n_f32(factor); 89336 int32x4_t shift0_4 = vdupq_n_s32(shift0); 89337 int32x4_t shift1_4 = vdupq_n_s32(shift1); 89338 for (i = 0; i < frameCount4; ++i) { 89339 int32x4_t lefti; 89340 int32x4_t righti; 89341 float32x4_t leftf; 89342 float32x4_t rightf; 89343 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); 89344 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); 89345 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); 89346 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); 89347 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); 89348 } 89349 for (i = (frameCount4 << 2); i < frameCount; ++i) { 89350 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; 89351 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; 89352 } 89353 } 89354 #endif 89355 static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) 89356 { 89357 #if defined(MA_DR_FLAC_SUPPORT_SSE2) 89358 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { 89359 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89360 } else 89361 #elif defined(MA_DR_FLAC_SUPPORT_NEON) 89362 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { 89363 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89364 } else 89365 #endif 89366 { 89367 #if 0 89368 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89369 #else 89370 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); 89371 #endif 89372 } 89373 } 89374 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) 89375 { 89376 ma_uint64 framesRead; 89377 ma_uint32 unusedBitsPerSample; 89378 if (pFlac == NULL || framesToRead == 0) { 89379 return 0; 89380 } 89381 if (pBufferOut == NULL) { 89382 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); 89383 } 89384 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); 89385 unusedBitsPerSample = 32 - pFlac->bitsPerSample; 89386 framesRead = 0; 89387 while (framesToRead > 0) { 89388 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { 89389 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { 89390 break; 89391 } 89392 } else { 89393 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); 89394 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; 89395 ma_uint64 frameCountThisIteration = framesToRead; 89396 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { 89397 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; 89398 } 89399 if (channelCount == 2) { 89400 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; 89401 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; 89402 switch (pFlac->currentFLACFrame.header.channelAssignment) 89403 { 89404 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: 89405 { 89406 ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 89407 } break; 89408 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: 89409 { 89410 ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 89411 } break; 89412 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: 89413 { 89414 ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 89415 } break; 89416 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: 89417 default: 89418 { 89419 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); 89420 } break; 89421 } 89422 } else { 89423 ma_uint64 i; 89424 for (i = 0; i < frameCountThisIteration; ++i) { 89425 unsigned int j; 89426 for (j = 0; j < channelCount; ++j) { 89427 ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); 89428 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); 89429 } 89430 } 89431 } 89432 framesRead += frameCountThisIteration; 89433 pBufferOut += frameCountThisIteration * channelCount; 89434 framesToRead -= frameCountThisIteration; 89435 pFlac->currentPCMFrame += frameCountThisIteration; 89436 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; 89437 } 89438 } 89439 return framesRead; 89440 } 89441 MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) 89442 { 89443 if (pFlac == NULL) { 89444 return MA_FALSE; 89445 } 89446 if (pFlac->currentPCMFrame == pcmFrameIndex) { 89447 return MA_TRUE; 89448 } 89449 if (pFlac->firstFLACFramePosInBytes == 0) { 89450 return MA_FALSE; 89451 } 89452 if (pcmFrameIndex == 0) { 89453 pFlac->currentPCMFrame = 0; 89454 return ma_dr_flac__seek_to_first_frame(pFlac); 89455 } else { 89456 ma_bool32 wasSuccessful = MA_FALSE; 89457 ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; 89458 if (pcmFrameIndex > pFlac->totalPCMFrameCount) { 89459 pcmFrameIndex = pFlac->totalPCMFrameCount; 89460 } 89461 if (pcmFrameIndex > pFlac->currentPCMFrame) { 89462 ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); 89463 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { 89464 pFlac->currentFLACFrame.pcmFramesRemaining -= offset; 89465 pFlac->currentPCMFrame = pcmFrameIndex; 89466 return MA_TRUE; 89467 } 89468 } else { 89469 ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); 89470 ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; 89471 ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; 89472 if (currentFLACFramePCMFramesConsumed > offsetAbs) { 89473 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; 89474 pFlac->currentPCMFrame = pcmFrameIndex; 89475 return MA_TRUE; 89476 } 89477 } 89478 #ifndef MA_DR_FLAC_NO_OGG 89479 if (pFlac->container == ma_dr_flac_container_ogg) 89480 { 89481 wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); 89482 } 89483 else 89484 #endif 89485 { 89486 if (!pFlac->_noSeekTableSeek) { 89487 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); 89488 } 89489 #if !defined(MA_DR_FLAC_NO_CRC) 89490 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { 89491 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); 89492 } 89493 #endif 89494 if (!wasSuccessful && !pFlac->_noBruteForceSeek) { 89495 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); 89496 } 89497 } 89498 if (wasSuccessful) { 89499 pFlac->currentPCMFrame = pcmFrameIndex; 89500 } else { 89501 if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { 89502 ma_dr_flac_seek_to_pcm_frame(pFlac, 0); 89503 } 89504 } 89505 return wasSuccessful; 89506 } 89507 } 89508 #define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ 89509 static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ 89510 { \ 89511 type* pSampleData = NULL; \ 89512 ma_uint64 totalPCMFrameCount; \ 89513 \ 89514 MA_DR_FLAC_ASSERT(pFlac != NULL); \ 89515 \ 89516 totalPCMFrameCount = pFlac->totalPCMFrameCount; \ 89517 \ 89518 if (totalPCMFrameCount == 0) { \ 89519 type buffer[4096]; \ 89520 ma_uint64 pcmFramesRead; \ 89521 size_t sampleDataBufferSize = sizeof(buffer); \ 89522 \ 89523 pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ 89524 if (pSampleData == NULL) { \ 89525 goto on_error; \ 89526 } \ 89527 \ 89528 while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ 89529 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ 89530 type* pNewSampleData; \ 89531 size_t newSampleDataBufferSize; \ 89532 \ 89533 newSampleDataBufferSize = sampleDataBufferSize * 2; \ 89534 pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ 89535 if (pNewSampleData == NULL) { \ 89536 ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ 89537 goto on_error; \ 89538 } \ 89539 \ 89540 sampleDataBufferSize = newSampleDataBufferSize; \ 89541 pSampleData = pNewSampleData; \ 89542 } \ 89543 \ 89544 MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ 89545 totalPCMFrameCount += pcmFramesRead; \ 89546 } \ 89547 \ 89548 \ 89549 MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ 89550 } else { \ 89551 ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ 89552 if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ 89553 goto on_error; \ 89554 } \ 89555 \ 89556 pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ 89557 if (pSampleData == NULL) { \ 89558 goto on_error; \ 89559 } \ 89560 \ 89561 totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ 89562 } \ 89563 \ 89564 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ 89565 if (channelsOut) *channelsOut = pFlac->channels; \ 89566 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ 89567 \ 89568 ma_dr_flac_close(pFlac); \ 89569 return pSampleData; \ 89570 \ 89571 on_error: \ 89572 ma_dr_flac_close(pFlac); \ 89573 return NULL; \ 89574 } 89575 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) 89576 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) 89577 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) 89578 MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 89579 { 89580 ma_dr_flac* pFlac; 89581 if (channelsOut) { 89582 *channelsOut = 0; 89583 } 89584 if (sampleRateOut) { 89585 *sampleRateOut = 0; 89586 } 89587 if (totalPCMFrameCountOut) { 89588 *totalPCMFrameCountOut = 0; 89589 } 89590 pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); 89591 if (pFlac == NULL) { 89592 return NULL; 89593 } 89594 return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); 89595 } 89596 MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 89597 { 89598 ma_dr_flac* pFlac; 89599 if (channelsOut) { 89600 *channelsOut = 0; 89601 } 89602 if (sampleRateOut) { 89603 *sampleRateOut = 0; 89604 } 89605 if (totalPCMFrameCountOut) { 89606 *totalPCMFrameCountOut = 0; 89607 } 89608 pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); 89609 if (pFlac == NULL) { 89610 return NULL; 89611 } 89612 return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); 89613 } 89614 MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) 89615 { 89616 ma_dr_flac* pFlac; 89617 if (channelsOut) { 89618 *channelsOut = 0; 89619 } 89620 if (sampleRateOut) { 89621 *sampleRateOut = 0; 89622 } 89623 if (totalPCMFrameCountOut) { 89624 *totalPCMFrameCountOut = 0; 89625 } 89626 pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); 89627 if (pFlac == NULL) { 89628 return NULL; 89629 } 89630 return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); 89631 } 89632 #ifndef MA_DR_FLAC_NO_STDIO 89633 MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89634 { 89635 ma_dr_flac* pFlac; 89636 if (sampleRate) { 89637 *sampleRate = 0; 89638 } 89639 if (channels) { 89640 *channels = 0; 89641 } 89642 if (totalPCMFrameCount) { 89643 *totalPCMFrameCount = 0; 89644 } 89645 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); 89646 if (pFlac == NULL) { 89647 return NULL; 89648 } 89649 return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); 89650 } 89651 MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89652 { 89653 ma_dr_flac* pFlac; 89654 if (sampleRate) { 89655 *sampleRate = 0; 89656 } 89657 if (channels) { 89658 *channels = 0; 89659 } 89660 if (totalPCMFrameCount) { 89661 *totalPCMFrameCount = 0; 89662 } 89663 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); 89664 if (pFlac == NULL) { 89665 return NULL; 89666 } 89667 return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); 89668 } 89669 MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89670 { 89671 ma_dr_flac* pFlac; 89672 if (sampleRate) { 89673 *sampleRate = 0; 89674 } 89675 if (channels) { 89676 *channels = 0; 89677 } 89678 if (totalPCMFrameCount) { 89679 *totalPCMFrameCount = 0; 89680 } 89681 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); 89682 if (pFlac == NULL) { 89683 return NULL; 89684 } 89685 return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); 89686 } 89687 #endif 89688 MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89689 { 89690 ma_dr_flac* pFlac; 89691 if (sampleRate) { 89692 *sampleRate = 0; 89693 } 89694 if (channels) { 89695 *channels = 0; 89696 } 89697 if (totalPCMFrameCount) { 89698 *totalPCMFrameCount = 0; 89699 } 89700 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); 89701 if (pFlac == NULL) { 89702 return NULL; 89703 } 89704 return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); 89705 } 89706 MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89707 { 89708 ma_dr_flac* pFlac; 89709 if (sampleRate) { 89710 *sampleRate = 0; 89711 } 89712 if (channels) { 89713 *channels = 0; 89714 } 89715 if (totalPCMFrameCount) { 89716 *totalPCMFrameCount = 0; 89717 } 89718 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); 89719 if (pFlac == NULL) { 89720 return NULL; 89721 } 89722 return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); 89723 } 89724 MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 89725 { 89726 ma_dr_flac* pFlac; 89727 if (sampleRate) { 89728 *sampleRate = 0; 89729 } 89730 if (channels) { 89731 *channels = 0; 89732 } 89733 if (totalPCMFrameCount) { 89734 *totalPCMFrameCount = 0; 89735 } 89736 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); 89737 if (pFlac == NULL) { 89738 return NULL; 89739 } 89740 return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); 89741 } 89742 MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 89743 { 89744 if (pAllocationCallbacks != NULL) { 89745 ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); 89746 } else { 89747 ma_dr_flac__free_default(p, NULL); 89748 } 89749 } 89750 MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) 89751 { 89752 if (pIter == NULL) { 89753 return; 89754 } 89755 pIter->countRemaining = commentCount; 89756 pIter->pRunningData = (const char*)pComments; 89757 } 89758 MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) 89759 { 89760 ma_int32 length; 89761 const char* pComment; 89762 if (pCommentLengthOut) { 89763 *pCommentLengthOut = 0; 89764 } 89765 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { 89766 return NULL; 89767 } 89768 length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); 89769 pIter->pRunningData += 4; 89770 pComment = pIter->pRunningData; 89771 pIter->pRunningData += length; 89772 pIter->countRemaining -= 1; 89773 if (pCommentLengthOut) { 89774 *pCommentLengthOut = length; 89775 } 89776 return pComment; 89777 } 89778 MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) 89779 { 89780 if (pIter == NULL) { 89781 return; 89782 } 89783 pIter->countRemaining = trackCount; 89784 pIter->pRunningData = (const char*)pTrackData; 89785 } 89786 MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) 89787 { 89788 ma_dr_flac_cuesheet_track cuesheetTrack; 89789 const char* pRunningData; 89790 ma_uint64 offsetHi; 89791 ma_uint64 offsetLo; 89792 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { 89793 return MA_FALSE; 89794 } 89795 pRunningData = pIter->pRunningData; 89796 offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; 89797 offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; 89798 cuesheetTrack.offset = offsetLo | (offsetHi << 32); 89799 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; 89800 MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; 89801 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; 89802 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; 89803 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; 89804 cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); 89805 pIter->pRunningData = pRunningData; 89806 pIter->countRemaining -= 1; 89807 if (pCuesheetTrack) { 89808 *pCuesheetTrack = cuesheetTrack; 89809 } 89810 return MA_TRUE; 89811 } 89812 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) 89813 #pragma GCC diagnostic pop 89814 #endif 89815 #endif 89816 /* dr_flac_c end */ 89817 #endif /* MA_DR_FLAC_IMPLEMENTATION */ 89818 #endif /* MA_NO_FLAC */ 89819 89820 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) 89821 #if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ 89822 /* dr_mp3_c begin */ 89823 #ifndef ma_dr_mp3_c 89824 #define ma_dr_mp3_c 89825 #include <stdlib.h> 89826 #include <string.h> 89827 #include <limits.h> 89828 MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) 89829 { 89830 if (pMajor) { 89831 *pMajor = MA_DR_MP3_VERSION_MAJOR; 89832 } 89833 if (pMinor) { 89834 *pMinor = MA_DR_MP3_VERSION_MINOR; 89835 } 89836 if (pRevision) { 89837 *pRevision = MA_DR_MP3_VERSION_REVISION; 89838 } 89839 } 89840 MA_API const char* ma_dr_mp3_version_string(void) 89841 { 89842 return MA_DR_MP3_VERSION_STRING; 89843 } 89844 #if defined(__TINYC__) 89845 #define MA_DR_MP3_NO_SIMD 89846 #endif 89847 #define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) 89848 #define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 89849 #ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 89850 #define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 89851 #endif 89852 #define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 89853 #define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 89854 #define MA_DR_MP3_SHORT_BLOCK_TYPE 2 89855 #define MA_DR_MP3_STOP_BLOCK_TYPE 3 89856 #define MA_DR_MP3_MODE_MONO 3 89857 #define MA_DR_MP3_MODE_JOINT_STEREO 1 89858 #define MA_DR_MP3_HDR_SIZE 4 89859 #define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) 89860 #define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) 89861 #define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) 89862 #define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) 89863 #define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) 89864 #define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) 89865 #define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) 89866 #define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) 89867 #define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) 89868 #define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) 89869 #define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) 89870 #define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) 89871 #define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) 89872 #define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) 89873 #define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) 89874 #define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) 89875 #define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) 89876 #define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 89877 #define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) 89878 #define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) 89879 #define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) 89880 #define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) 89881 #if !defined(MA_DR_MP3_NO_SIMD) 89882 #if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) 89883 #define MA_DR_MP3_ONLY_SIMD 89884 #endif 89885 #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) 89886 #if defined(_MSC_VER) 89887 #include <intrin.h> 89888 #endif 89889 #include <emmintrin.h> 89890 #define MA_DR_MP3_HAVE_SSE 1 89891 #define MA_DR_MP3_HAVE_SIMD 1 89892 #define MA_DR_MP3_VSTORE _mm_storeu_ps 89893 #define MA_DR_MP3_VLD _mm_loadu_ps 89894 #define MA_DR_MP3_VSET _mm_set1_ps 89895 #define MA_DR_MP3_VADD _mm_add_ps 89896 #define MA_DR_MP3_VSUB _mm_sub_ps 89897 #define MA_DR_MP3_VMUL _mm_mul_ps 89898 #define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) 89899 #define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) 89900 #define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) 89901 #define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) 89902 typedef __m128 ma_dr_mp3_f4; 89903 #if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) 89904 #define ma_dr_mp3_cpuid __cpuid 89905 #else 89906 static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) 89907 { 89908 #if defined(__PIC__) 89909 __asm__ __volatile__( 89910 #if defined(__x86_64__) 89911 "push %%rbx\n" 89912 "cpuid\n" 89913 "xchgl %%ebx, %1\n" 89914 "pop %%rbx\n" 89915 #else 89916 "xchgl %%ebx, %1\n" 89917 "cpuid\n" 89918 "xchgl %%ebx, %1\n" 89919 #endif 89920 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) 89921 : "a" (InfoType)); 89922 #else 89923 __asm__ __volatile__( 89924 "cpuid" 89925 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) 89926 : "a" (InfoType)); 89927 #endif 89928 } 89929 #endif 89930 static int ma_dr_mp3_have_simd(void) 89931 { 89932 #ifdef MA_DR_MP3_ONLY_SIMD 89933 return 1; 89934 #else 89935 static int g_have_simd; 89936 int CPUInfo[4]; 89937 #ifdef MINIMP3_TEST 89938 static int g_counter; 89939 if (g_counter++ > 100) 89940 return 0; 89941 #endif 89942 if (g_have_simd) 89943 goto end; 89944 ma_dr_mp3_cpuid(CPUInfo, 0); 89945 if (CPUInfo[0] > 0) 89946 { 89947 ma_dr_mp3_cpuid(CPUInfo, 1); 89948 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; 89949 return g_have_simd - 1; 89950 } 89951 end: 89952 return g_have_simd - 1; 89953 #endif 89954 } 89955 #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) 89956 #include <arm_neon.h> 89957 #define MA_DR_MP3_HAVE_SSE 0 89958 #define MA_DR_MP3_HAVE_SIMD 1 89959 #define MA_DR_MP3_VSTORE vst1q_f32 89960 #define MA_DR_MP3_VLD vld1q_f32 89961 #define MA_DR_MP3_VSET vmovq_n_f32 89962 #define MA_DR_MP3_VADD vaddq_f32 89963 #define MA_DR_MP3_VSUB vsubq_f32 89964 #define MA_DR_MP3_VMUL vmulq_f32 89965 #define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) 89966 #define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) 89967 #define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) 89968 #define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) 89969 typedef float32x4_t ma_dr_mp3_f4; 89970 static int ma_dr_mp3_have_simd(void) 89971 { 89972 return 1; 89973 } 89974 #else 89975 #define MA_DR_MP3_HAVE_SSE 0 89976 #define MA_DR_MP3_HAVE_SIMD 0 89977 #ifdef MA_DR_MP3_ONLY_SIMD 89978 #error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled 89979 #endif 89980 #endif 89981 #else 89982 #define MA_DR_MP3_HAVE_SIMD 0 89983 #endif 89984 #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) 89985 #define MA_DR_MP3_HAVE_ARMV6 1 89986 static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) 89987 { 89988 ma_int32 x = 0; 89989 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); 89990 return x; 89991 } 89992 #else 89993 #define MA_DR_MP3_HAVE_ARMV6 0 89994 #endif 89995 #ifndef MA_DR_MP3_ASSERT 89996 #include <assert.h> 89997 #define MA_DR_MP3_ASSERT(expression) assert(expression) 89998 #endif 89999 #ifndef MA_DR_MP3_COPY_MEMORY 90000 #define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) 90001 #endif 90002 #ifndef MA_DR_MP3_MOVE_MEMORY 90003 #define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) 90004 #endif 90005 #ifndef MA_DR_MP3_ZERO_MEMORY 90006 #define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) 90007 #endif 90008 #define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) 90009 #ifndef MA_DR_MP3_MALLOC 90010 #define MA_DR_MP3_MALLOC(sz) malloc((sz)) 90011 #endif 90012 #ifndef MA_DR_MP3_REALLOC 90013 #define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) 90014 #endif 90015 #ifndef MA_DR_MP3_FREE 90016 #define MA_DR_MP3_FREE(p) free((p)) 90017 #endif 90018 typedef struct 90019 { 90020 const ma_uint8 *buf; 90021 int pos, limit; 90022 } ma_dr_mp3_bs; 90023 typedef struct 90024 { 90025 float scf[3*64]; 90026 ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; 90027 } ma_dr_mp3_L12_scale_info; 90028 typedef struct 90029 { 90030 ma_uint8 tab_offset, code_tab_width, band_count; 90031 } ma_dr_mp3_L12_subband_alloc; 90032 typedef struct 90033 { 90034 const ma_uint8 *sfbtab; 90035 ma_uint16 part_23_length, big_values, scalefac_compress; 90036 ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; 90037 ma_uint8 table_select[3], region_count[3], subblock_gain[3]; 90038 ma_uint8 preflag, scalefac_scale, count1_table, scfsi; 90039 } ma_dr_mp3_L3_gr_info; 90040 typedef struct 90041 { 90042 ma_dr_mp3_bs bs; 90043 ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; 90044 ma_dr_mp3_L3_gr_info gr_info[4]; 90045 float grbuf[2][576], scf[40], syn[18 + 15][2*32]; 90046 ma_uint8 ist_pos[2][39]; 90047 } ma_dr_mp3dec_scratch; 90048 static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) 90049 { 90050 bs->buf = data; 90051 bs->pos = 0; 90052 bs->limit = bytes*8; 90053 } 90054 static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) 90055 { 90056 ma_uint32 next, cache = 0, s = bs->pos & 7; 90057 int shl = n + s; 90058 const ma_uint8 *p = bs->buf + (bs->pos >> 3); 90059 if ((bs->pos += n) > bs->limit) 90060 return 0; 90061 next = *p++ & (255 >> s); 90062 while ((shl -= 8) > 0) 90063 { 90064 cache |= next << shl; 90065 next = *p++; 90066 } 90067 return cache | (next >> -shl); 90068 } 90069 static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) 90070 { 90071 return h[0] == 0xff && 90072 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && 90073 (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && 90074 (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && 90075 (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); 90076 } 90077 static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) 90078 { 90079 return ma_dr_mp3_hdr_valid(h2) && 90080 ((h1[1] ^ h2[1]) & 0xFE) == 0 && 90081 ((h1[2] ^ h2[2]) & 0x0C) == 0 && 90082 !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); 90083 } 90084 static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) 90085 { 90086 static const ma_uint8 halfrate[2][3][15] = { 90087 { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, 90088 { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, 90089 }; 90090 return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; 90091 } 90092 static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) 90093 { 90094 static const unsigned g_hz[3] = { 44100, 48000, 32000 }; 90095 return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); 90096 } 90097 static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) 90098 { 90099 return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); 90100 } 90101 static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) 90102 { 90103 int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); 90104 if (MA_DR_MP3_HDR_IS_LAYER_1(h)) 90105 { 90106 frame_bytes &= ~3; 90107 } 90108 return frame_bytes ? frame_bytes : free_format_size; 90109 } 90110 static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) 90111 { 90112 return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; 90113 } 90114 #ifndef MA_DR_MP3_ONLY_MP3 90115 static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) 90116 { 90117 const ma_dr_mp3_L12_subband_alloc *alloc; 90118 int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); 90119 int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; 90120 if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) 90121 { 90122 static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; 90123 alloc = g_alloc_L1; 90124 nbands = 32; 90125 } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) 90126 { 90127 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; 90128 alloc = g_alloc_L2M2; 90129 nbands = 30; 90130 } else 90131 { 90132 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; 90133 int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); 90134 unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); 90135 if (!kbps) 90136 { 90137 kbps = 192; 90138 } 90139 alloc = g_alloc_L2M1; 90140 nbands = 27; 90141 if (kbps < 56) 90142 { 90143 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; 90144 alloc = g_alloc_L2M1_lowrate; 90145 nbands = sample_rate_idx == 2 ? 12 : 8; 90146 } else if (kbps >= 96 && sample_rate_idx != 1) 90147 { 90148 nbands = 30; 90149 } 90150 } 90151 sci->total_bands = (ma_uint8)nbands; 90152 sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); 90153 return alloc; 90154 } 90155 static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) 90156 { 90157 static const float g_deq_L12[18*3] = { 90158 #define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x 90159 MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) 90160 }; 90161 int i, m; 90162 for (i = 0; i < bands; i++) 90163 { 90164 float s = 0; 90165 int ba = *pba++; 90166 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; 90167 for (m = 4; m; m >>= 1) 90168 { 90169 if (mask & m) 90170 { 90171 int b = ma_dr_mp3_bs_get_bits(bs, 6); 90172 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); 90173 } 90174 *scf++ = s; 90175 } 90176 } 90177 } 90178 static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) 90179 { 90180 static const ma_uint8 g_bitalloc_code_tab[] = { 90181 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, 90182 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, 90183 0,17,18, 3,19,4,5,16, 90184 0,17,18,16, 90185 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, 90186 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, 90187 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 90188 }; 90189 const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); 90190 int i, k = 0, ba_bits = 0; 90191 const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; 90192 for (i = 0; i < sci->total_bands; i++) 90193 { 90194 ma_uint8 ba; 90195 if (i == k) 90196 { 90197 k += subband_alloc->band_count; 90198 ba_bits = subband_alloc->code_tab_width; 90199 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; 90200 subband_alloc++; 90201 } 90202 ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; 90203 sci->bitalloc[2*i] = ba; 90204 if (i < sci->stereo_bands) 90205 { 90206 ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; 90207 } 90208 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; 90209 } 90210 for (i = 0; i < 2*sci->total_bands; i++) 90211 { 90212 sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); 90213 } 90214 ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); 90215 for (i = sci->stereo_bands; i < sci->total_bands; i++) 90216 { 90217 sci->bitalloc[2*i + 1] = 0; 90218 } 90219 } 90220 static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) 90221 { 90222 int i, j, k, choff = 576; 90223 for (j = 0; j < 4; j++) 90224 { 90225 float *dst = grbuf + group_size*j; 90226 for (i = 0; i < 2*sci->total_bands; i++) 90227 { 90228 int ba = sci->bitalloc[i]; 90229 if (ba != 0) 90230 { 90231 if (ba < 17) 90232 { 90233 int half = (1 << (ba - 1)) - 1; 90234 for (k = 0; k < group_size; k++) 90235 { 90236 dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); 90237 } 90238 } else 90239 { 90240 unsigned mod = (2 << (ba - 17)) + 1; 90241 unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); 90242 for (k = 0; k < group_size; k++, code /= mod) 90243 { 90244 dst[k] = (float)((int)(code % mod - mod/2)); 90245 } 90246 } 90247 } 90248 dst += choff; 90249 choff = 18 - choff; 90250 } 90251 } 90252 return group_size*4; 90253 } 90254 static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) 90255 { 90256 int i, k; 90257 MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); 90258 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) 90259 { 90260 for (k = 0; k < 12; k++) 90261 { 90262 dst[k + 0] *= scf[0]; 90263 dst[k + 576] *= scf[3]; 90264 } 90265 } 90266 } 90267 #endif 90268 static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) 90269 { 90270 static const ma_uint8 g_scf_long[8][23] = { 90271 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, 90272 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, 90273 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, 90274 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, 90275 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, 90276 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, 90277 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, 90278 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } 90279 }; 90280 static const ma_uint8 g_scf_short[8][40] = { 90281 { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, 90282 { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, 90283 { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, 90284 { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, 90285 { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, 90286 { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, 90287 { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, 90288 { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } 90289 }; 90290 static const ma_uint8 g_scf_mixed[8][40] = { 90291 { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, 90292 { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, 90293 { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, 90294 { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, 90295 { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, 90296 { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, 90297 { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, 90298 { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } 90299 }; 90300 unsigned tables, scfsi = 0; 90301 int main_data_begin, part_23_sum = 0; 90302 int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; 90303 int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); 90304 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) 90305 { 90306 gr_count *= 2; 90307 main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); 90308 scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); 90309 } else 90310 { 90311 main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; 90312 } 90313 do 90314 { 90315 if (MA_DR_MP3_HDR_IS_MONO(hdr)) 90316 { 90317 scfsi <<= 4; 90318 } 90319 gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); 90320 part_23_sum += gr->part_23_length; 90321 gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); 90322 if (gr->big_values > 288) 90323 { 90324 return -1; 90325 } 90326 gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); 90327 gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); 90328 gr->sfbtab = g_scf_long[sr_idx]; 90329 gr->n_long_sfb = 22; 90330 gr->n_short_sfb = 0; 90331 if (ma_dr_mp3_bs_get_bits(bs, 1)) 90332 { 90333 gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); 90334 if (!gr->block_type) 90335 { 90336 return -1; 90337 } 90338 gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); 90339 gr->region_count[0] = 7; 90340 gr->region_count[1] = 255; 90341 if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) 90342 { 90343 scfsi &= 0x0F0F; 90344 if (!gr->mixed_block_flag) 90345 { 90346 gr->region_count[0] = 8; 90347 gr->sfbtab = g_scf_short[sr_idx]; 90348 gr->n_long_sfb = 0; 90349 gr->n_short_sfb = 39; 90350 } else 90351 { 90352 gr->sfbtab = g_scf_mixed[sr_idx]; 90353 gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; 90354 gr->n_short_sfb = 30; 90355 } 90356 } 90357 tables = ma_dr_mp3_bs_get_bits(bs, 10); 90358 tables <<= 5; 90359 gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); 90360 gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); 90361 gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); 90362 } else 90363 { 90364 gr->block_type = 0; 90365 gr->mixed_block_flag = 0; 90366 tables = ma_dr_mp3_bs_get_bits(bs, 15); 90367 gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); 90368 gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); 90369 gr->region_count[2] = 255; 90370 } 90371 gr->table_select[0] = (ma_uint8)(tables >> 10); 90372 gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); 90373 gr->table_select[2] = (ma_uint8)((tables) & 31); 90374 gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); 90375 gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); 90376 gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); 90377 gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); 90378 scfsi <<= 4; 90379 gr++; 90380 } while(--gr_count); 90381 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) 90382 { 90383 return -1; 90384 } 90385 return main_data_begin; 90386 } 90387 static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) 90388 { 90389 int i, k; 90390 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) 90391 { 90392 int cnt = scf_count[i]; 90393 if (scfsi & 8) 90394 { 90395 MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); 90396 } else 90397 { 90398 int bits = scf_size[i]; 90399 if (!bits) 90400 { 90401 MA_DR_MP3_ZERO_MEMORY(scf, cnt); 90402 MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); 90403 } else 90404 { 90405 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; 90406 for (k = 0; k < cnt; k++) 90407 { 90408 int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); 90409 ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); 90410 scf[k] = (ma_uint8)s; 90411 } 90412 } 90413 } 90414 ist_pos += cnt; 90415 scf += cnt; 90416 } 90417 scf[0] = scf[1] = scf[2] = 0; 90418 } 90419 static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) 90420 { 90421 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; 90422 int e; 90423 do 90424 { 90425 e = MA_DR_MP3_MIN(30*4, exp_q2); 90426 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); 90427 } while ((exp_q2 -= e) > 0); 90428 return y; 90429 } 90430 static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) 90431 { 90432 static const ma_uint8 g_scf_partitions[3][28] = { 90433 { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, 90434 { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, 90435 { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } 90436 }; 90437 const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; 90438 ma_uint8 scf_size[4], iscf[40]; 90439 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; 90440 float gain; 90441 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) 90442 { 90443 static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; 90444 int part = g_scfc_decode[gr->scalefac_compress]; 90445 scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); 90446 scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); 90447 } else 90448 { 90449 static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; 90450 int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; 90451 sfc = gr->scalefac_compress >> ist; 90452 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) 90453 { 90454 for (modprod = 1, i = 3; i >= 0; i--) 90455 { 90456 scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); 90457 modprod *= g_mod[k + i]; 90458 } 90459 } 90460 scf_partition += k; 90461 scfsi = -16; 90462 } 90463 ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); 90464 if (gr->n_short_sfb) 90465 { 90466 int sh = 3 - scf_shift; 90467 for (i = 0; i < gr->n_short_sfb; i += 3) 90468 { 90469 iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); 90470 iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); 90471 iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); 90472 } 90473 } else if (gr->preflag) 90474 { 90475 static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; 90476 for (i = 0; i < 10; i++) 90477 { 90478 iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); 90479 } 90480 } 90481 gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); 90482 gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); 90483 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) 90484 { 90485 scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); 90486 } 90487 } 90488 static const float g_ma_dr_mp3_pow43[129 + 16] = { 90489 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 90490 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f 90491 }; 90492 static float ma_dr_mp3_L3_pow_43(int x) 90493 { 90494 float frac; 90495 int sign, mult = 256; 90496 if (x < 129) 90497 { 90498 return g_ma_dr_mp3_pow43[16 + x]; 90499 } 90500 if (x < 1024) 90501 { 90502 mult = 16; 90503 x <<= 3; 90504 } 90505 sign = 2*x & 64; 90506 frac = (float)((x & 63) - sign) / ((x & ~63) + sign); 90507 return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; 90508 } 90509 static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) 90510 { 90511 static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90512 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 90513 -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, 90514 -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, 90515 -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, 90516 -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, 90517 -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, 90518 -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, 90519 -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, 90520 -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, 90521 -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, 90522 -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, 90523 -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, 90524 -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, 90525 -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, 90526 -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; 90527 static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; 90528 static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; 90529 static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; 90530 static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; 90531 #define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) 90532 #define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } 90533 #define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } 90534 #define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) 90535 float one = 0.0f; 90536 int ireg = 0, big_val_cnt = gr_info->big_values; 90537 const ma_uint8 *sfb = gr_info->sfbtab; 90538 const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; 90539 ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); 90540 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; 90541 bs_next_ptr += 4; 90542 while (big_val_cnt > 0) 90543 { 90544 int tab_num = gr_info->table_select[ireg]; 90545 int sfb_cnt = gr_info->region_count[ireg++]; 90546 const ma_int16 *codebook = tabs + tabindex[tab_num]; 90547 int linbits = g_linbits[tab_num]; 90548 if (linbits) 90549 { 90550 do 90551 { 90552 np = *sfb++ / 2; 90553 pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); 90554 one = *scf++; 90555 do 90556 { 90557 int j, w = 5; 90558 int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; 90559 while (leaf < 0) 90560 { 90561 MA_DR_MP3_FLUSH_BITS(w); 90562 w = leaf & 7; 90563 leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; 90564 } 90565 MA_DR_MP3_FLUSH_BITS(leaf >> 8); 90566 for (j = 0; j < 2; j++, dst++, leaf >>= 4) 90567 { 90568 int lsb = leaf & 0x0F; 90569 if (lsb == 15) 90570 { 90571 lsb += MA_DR_MP3_PEEK_BITS(linbits); 90572 MA_DR_MP3_FLUSH_BITS(linbits); 90573 MA_DR_MP3_CHECK_BITS; 90574 *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); 90575 } else 90576 { 90577 *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; 90578 } 90579 MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); 90580 } 90581 MA_DR_MP3_CHECK_BITS; 90582 } while (--pairs_to_decode); 90583 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); 90584 } else 90585 { 90586 do 90587 { 90588 np = *sfb++ / 2; 90589 pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); 90590 one = *scf++; 90591 do 90592 { 90593 int j, w = 5; 90594 int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; 90595 while (leaf < 0) 90596 { 90597 MA_DR_MP3_FLUSH_BITS(w); 90598 w = leaf & 7; 90599 leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; 90600 } 90601 MA_DR_MP3_FLUSH_BITS(leaf >> 8); 90602 for (j = 0; j < 2; j++, dst++, leaf >>= 4) 90603 { 90604 int lsb = leaf & 0x0F; 90605 *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; 90606 MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); 90607 } 90608 MA_DR_MP3_CHECK_BITS; 90609 } while (--pairs_to_decode); 90610 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); 90611 } 90612 } 90613 for (np = 1 - big_val_cnt;; dst += 4) 90614 { 90615 const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; 90616 int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; 90617 if (!(leaf & 8)) 90618 { 90619 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; 90620 } 90621 MA_DR_MP3_FLUSH_BITS(leaf & 7); 90622 if (MA_DR_MP3_BSPOS > layer3gr_limit) 90623 { 90624 break; 90625 } 90626 #define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } 90627 #define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } 90628 MA_DR_MP3_RELOAD_SCALEFACTOR; 90629 MA_DR_MP3_DEQ_COUNT1(0); 90630 MA_DR_MP3_DEQ_COUNT1(1); 90631 MA_DR_MP3_RELOAD_SCALEFACTOR; 90632 MA_DR_MP3_DEQ_COUNT1(2); 90633 MA_DR_MP3_DEQ_COUNT1(3); 90634 MA_DR_MP3_CHECK_BITS; 90635 } 90636 bs->pos = layer3gr_limit; 90637 } 90638 static void ma_dr_mp3_L3_midside_stereo(float *left, int n) 90639 { 90640 int i = 0; 90641 float *right = left + 576; 90642 #if MA_DR_MP3_HAVE_SIMD 90643 if (ma_dr_mp3_have_simd()) 90644 { 90645 for (; i < n - 3; i += 4) 90646 { 90647 ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); 90648 ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); 90649 MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); 90650 MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); 90651 } 90652 #ifdef __GNUC__ 90653 if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) 90654 return; 90655 #endif 90656 } 90657 #endif 90658 for (; i < n; i++) 90659 { 90660 float a = left[i]; 90661 float b = right[i]; 90662 left[i] = a + b; 90663 right[i] = a - b; 90664 } 90665 } 90666 static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) 90667 { 90668 int i; 90669 for (i = 0; i < n; i++) 90670 { 90671 left[i + 576] = left[i]*kr; 90672 left[i] = left[i]*kl; 90673 } 90674 } 90675 static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) 90676 { 90677 int i, k; 90678 max_band[0] = max_band[1] = max_band[2] = -1; 90679 for (i = 0; i < nbands; i++) 90680 { 90681 for (k = 0; k < sfb[i]; k += 2) 90682 { 90683 if (right[k] != 0 || right[k + 1] != 0) 90684 { 90685 max_band[i % 3] = i; 90686 break; 90687 } 90688 } 90689 right += sfb[i]; 90690 } 90691 } 90692 static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) 90693 { 90694 static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; 90695 unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; 90696 for (i = 0; sfb[i]; i++) 90697 { 90698 unsigned ipos = ist_pos[i]; 90699 if ((int)i > max_band[i % 3] && ipos < max_pos) 90700 { 90701 float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; 90702 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) 90703 { 90704 kl = g_pan[2*ipos]; 90705 kr = g_pan[2*ipos + 1]; 90706 } else 90707 { 90708 kl = 1; 90709 kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); 90710 if (ipos & 1) 90711 { 90712 kl = kr; 90713 kr = 1; 90714 } 90715 } 90716 ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); 90717 } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) 90718 { 90719 ma_dr_mp3_L3_midside_stereo(left, sfb[i]); 90720 } 90721 left += sfb[i]; 90722 } 90723 } 90724 static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) 90725 { 90726 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; 90727 int i, max_blocks = gr->n_short_sfb ? 3 : 1; 90728 ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); 90729 if (gr->n_long_sfb) 90730 { 90731 max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); 90732 } 90733 for (i = 0; i < max_blocks; i++) 90734 { 90735 int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; 90736 int itop = n_sfb - max_blocks + i; 90737 int prev = itop - max_blocks; 90738 ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); 90739 } 90740 ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); 90741 } 90742 static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) 90743 { 90744 int i, len; 90745 float *src = grbuf, *dst = scratch; 90746 for (;0 != (len = *sfb); sfb += 3, src += 2*len) 90747 { 90748 for (i = 0; i < len; i++, src++) 90749 { 90750 *dst++ = src[0*len]; 90751 *dst++ = src[1*len]; 90752 *dst++ = src[2*len]; 90753 } 90754 } 90755 MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); 90756 } 90757 static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) 90758 { 90759 static const float g_aa[2][8] = { 90760 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, 90761 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} 90762 }; 90763 for (; nbands > 0; nbands--, grbuf += 18) 90764 { 90765 int i = 0; 90766 #if MA_DR_MP3_HAVE_SIMD 90767 if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) 90768 { 90769 ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); 90770 ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); 90771 ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); 90772 ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); 90773 vd = MA_DR_MP3_VREV(vd); 90774 MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); 90775 vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); 90776 MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); 90777 } 90778 #endif 90779 #ifndef MA_DR_MP3_ONLY_SIMD 90780 for(; i < 8; i++) 90781 { 90782 float u = grbuf[18 + i]; 90783 float d = grbuf[17 - i]; 90784 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; 90785 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; 90786 } 90787 #endif 90788 } 90789 } 90790 static void ma_dr_mp3_L3_dct3_9(float *y) 90791 { 90792 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; 90793 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; 90794 t0 = s0 + s6*0.5f; 90795 s0 -= s6; 90796 t4 = (s4 + s2)*0.93969262f; 90797 t2 = (s8 + s2)*0.76604444f; 90798 s6 = (s4 - s8)*0.17364818f; 90799 s4 += s8 - s2; 90800 s2 = s0 - s4*0.5f; 90801 y[4] = s4 + s0; 90802 s8 = t0 - t2 + s6; 90803 s0 = t0 - t4 + t2; 90804 s4 = t0 + t4 - s6; 90805 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; 90806 s3 *= 0.86602540f; 90807 t0 = (s5 + s1)*0.98480775f; 90808 t4 = (s5 - s7)*0.34202014f; 90809 t2 = (s1 + s7)*0.64278761f; 90810 s1 = (s1 - s5 - s7)*0.86602540f; 90811 s5 = t0 - s3 - t2; 90812 s7 = t4 - s3 - t0; 90813 s3 = t4 + s3 - t2; 90814 y[0] = s4 - s7; 90815 y[1] = s2 + s1; 90816 y[2] = s0 - s3; 90817 y[3] = s8 + s5; 90818 y[5] = s8 - s5; 90819 y[6] = s0 + s3; 90820 y[7] = s2 - s1; 90821 y[8] = s4 + s7; 90822 } 90823 static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) 90824 { 90825 int i, j; 90826 static const float g_twid9[18] = { 90827 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f 90828 }; 90829 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) 90830 { 90831 float co[9], si[9]; 90832 co[0] = -grbuf[0]; 90833 si[0] = grbuf[17]; 90834 for (i = 0; i < 4; i++) 90835 { 90836 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; 90837 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; 90838 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; 90839 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); 90840 } 90841 ma_dr_mp3_L3_dct3_9(co); 90842 ma_dr_mp3_L3_dct3_9(si); 90843 si[1] = -si[1]; 90844 si[3] = -si[3]; 90845 si[5] = -si[5]; 90846 si[7] = -si[7]; 90847 i = 0; 90848 #if MA_DR_MP3_HAVE_SIMD 90849 if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) 90850 { 90851 ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); 90852 ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); 90853 ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); 90854 ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); 90855 ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); 90856 ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); 90857 ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); 90858 ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); 90859 MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); 90860 MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); 90861 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); 90862 MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); 90863 } 90864 #endif 90865 for (; i < 9; i++) 90866 { 90867 float ovl = overlap[i]; 90868 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; 90869 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; 90870 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; 90871 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; 90872 } 90873 } 90874 } 90875 static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) 90876 { 90877 float m1 = x1*0.86602540f; 90878 float a1 = x0 - x2*0.5f; 90879 dst[1] = x0 + x2; 90880 dst[0] = a1 + m1; 90881 dst[2] = a1 - m1; 90882 } 90883 static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) 90884 { 90885 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; 90886 float co[3], si[3]; 90887 int i; 90888 ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); 90889 ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); 90890 si[1] = -si[1]; 90891 for (i = 0; i < 3; i++) 90892 { 90893 float ovl = overlap[i]; 90894 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; 90895 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; 90896 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; 90897 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; 90898 } 90899 } 90900 static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) 90901 { 90902 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) 90903 { 90904 float tmp[18]; 90905 MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); 90906 MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); 90907 ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); 90908 ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); 90909 ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); 90910 } 90911 } 90912 static void ma_dr_mp3_L3_change_sign(float *grbuf) 90913 { 90914 int b, i; 90915 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) 90916 for (i = 1; i < 18; i += 2) 90917 grbuf[i] = -grbuf[i]; 90918 } 90919 static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) 90920 { 90921 static const float g_mdct_window[2][18] = { 90922 { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, 90923 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } 90924 }; 90925 if (n_long_bands) 90926 { 90927 ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); 90928 grbuf += 18*n_long_bands; 90929 overlap += 9*n_long_bands; 90930 } 90931 if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) 90932 ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); 90933 else 90934 ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); 90935 } 90936 static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) 90937 { 90938 int pos = (s->bs.pos + 7)/8u; 90939 int remains = s->bs.limit/8u - pos; 90940 if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) 90941 { 90942 pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; 90943 remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; 90944 } 90945 if (remains > 0) 90946 { 90947 MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); 90948 } 90949 h->reserv = remains; 90950 } 90951 static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) 90952 { 90953 int frame_bytes = (bs->limit - bs->pos)/8; 90954 int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); 90955 MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); 90956 MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); 90957 ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); 90958 return h->reserv >= main_data_begin; 90959 } 90960 static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) 90961 { 90962 int ch; 90963 for (ch = 0; ch < nch; ch++) 90964 { 90965 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; 90966 ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); 90967 ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); 90968 } 90969 if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) 90970 { 90971 ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); 90972 } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) 90973 { 90974 ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); 90975 } 90976 for (ch = 0; ch < nch; ch++, gr_info++) 90977 { 90978 int aa_bands = 31; 90979 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); 90980 if (gr_info->n_short_sfb) 90981 { 90982 aa_bands = n_long_bands - 1; 90983 ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); 90984 } 90985 ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); 90986 ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); 90987 ma_dr_mp3_L3_change_sign(s->grbuf[ch]); 90988 } 90989 } 90990 static void ma_dr_mp3d_DCT_II(float *grbuf, int n) 90991 { 90992 static const float g_sec[24] = { 90993 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f 90994 }; 90995 int i, k = 0; 90996 #if MA_DR_MP3_HAVE_SIMD 90997 if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) 90998 { 90999 ma_dr_mp3_f4 t[4][8], *x; 91000 float *y = grbuf + k; 91001 for (x = t[0], i = 0; i < 8; i++, x++) 91002 { 91003 ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); 91004 ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); 91005 ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); 91006 ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); 91007 ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); 91008 ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); 91009 ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); 91010 ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); 91011 x[0] = MA_DR_MP3_VADD(t0, t1); 91012 x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); 91013 x[16] = MA_DR_MP3_VADD(t3, t2); 91014 x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); 91015 } 91016 for (x = t[0], i = 0; i < 4; i++, x += 8) 91017 { 91018 ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; 91019 xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); 91020 x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); 91021 x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); 91022 x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); 91023 x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); 91024 x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); 91025 x[0] = MA_DR_MP3_VADD(x0, x1); 91026 x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); 91027 x5 = MA_DR_MP3_VADD(x5, x6); 91028 x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); 91029 x7 = MA_DR_MP3_VADD(x7, xt); 91030 x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); 91031 x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); 91032 x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); 91033 x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); 91034 x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); 91035 x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); 91036 x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); 91037 x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); 91038 x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); 91039 x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); 91040 x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); 91041 } 91042 if (k > n - 3) 91043 { 91044 #if MA_DR_MP3_HAVE_SSE 91045 #define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) 91046 #else 91047 #define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) 91048 #endif 91049 for (i = 0; i < 7; i++, y += 4*18) 91050 { 91051 ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); 91052 MA_DR_MP3_VSAVE2(0, t[0][i]); 91053 MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); 91054 MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); 91055 MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); 91056 } 91057 MA_DR_MP3_VSAVE2(0, t[0][7]); 91058 MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); 91059 MA_DR_MP3_VSAVE2(2, t[1][7]); 91060 MA_DR_MP3_VSAVE2(3, t[3][7]); 91061 } else 91062 { 91063 #define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) 91064 for (i = 0; i < 7; i++, y += 4*18) 91065 { 91066 ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); 91067 MA_DR_MP3_VSAVE4(0, t[0][i]); 91068 MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); 91069 MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); 91070 MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); 91071 } 91072 MA_DR_MP3_VSAVE4(0, t[0][7]); 91073 MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); 91074 MA_DR_MP3_VSAVE4(2, t[1][7]); 91075 MA_DR_MP3_VSAVE4(3, t[3][7]); 91076 } 91077 } else 91078 #endif 91079 #ifdef MA_DR_MP3_ONLY_SIMD 91080 {} 91081 #else 91082 for (; k < n; k++) 91083 { 91084 float t[4][8], *x, *y = grbuf + k; 91085 for (x = t[0], i = 0; i < 8; i++, x++) 91086 { 91087 float x0 = y[i*18]; 91088 float x1 = y[(15 - i)*18]; 91089 float x2 = y[(16 + i)*18]; 91090 float x3 = y[(31 - i)*18]; 91091 float t0 = x0 + x3; 91092 float t1 = x1 + x2; 91093 float t2 = (x1 - x2)*g_sec[3*i + 0]; 91094 float t3 = (x0 - x3)*g_sec[3*i + 1]; 91095 x[0] = t0 + t1; 91096 x[8] = (t0 - t1)*g_sec[3*i + 2]; 91097 x[16] = t3 + t2; 91098 x[24] = (t3 - t2)*g_sec[3*i + 2]; 91099 } 91100 for (x = t[0], i = 0; i < 4; i++, x += 8) 91101 { 91102 float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; 91103 xt = x0 - x7; x0 += x7; 91104 x7 = x1 - x6; x1 += x6; 91105 x6 = x2 - x5; x2 += x5; 91106 x5 = x3 - x4; x3 += x4; 91107 x4 = x0 - x3; x0 += x3; 91108 x3 = x1 - x2; x1 += x2; 91109 x[0] = x0 + x1; 91110 x[4] = (x0 - x1)*0.70710677f; 91111 x5 = x5 + x6; 91112 x6 = (x6 + x7)*0.70710677f; 91113 x7 = x7 + xt; 91114 x3 = (x3 + x4)*0.70710677f; 91115 x5 -= x7*0.198912367f; 91116 x7 += x5*0.382683432f; 91117 x5 -= x7*0.198912367f; 91118 x0 = xt - x6; xt += x6; 91119 x[1] = (xt + x7)*0.50979561f; 91120 x[2] = (x4 + x3)*0.54119611f; 91121 x[3] = (x0 - x5)*0.60134488f; 91122 x[5] = (x0 + x5)*0.89997619f; 91123 x[6] = (x4 - x3)*1.30656302f; 91124 x[7] = (xt - x7)*2.56291556f; 91125 } 91126 for (i = 0; i < 7; i++, y += 4*18) 91127 { 91128 y[0*18] = t[0][i]; 91129 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; 91130 y[2*18] = t[1][i] + t[1][i + 1]; 91131 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; 91132 } 91133 y[0*18] = t[0][7]; 91134 y[1*18] = t[2][7] + t[3][7]; 91135 y[2*18] = t[1][7]; 91136 y[3*18] = t[3][7]; 91137 } 91138 #endif 91139 } 91140 #ifndef MA_DR_MP3_FLOAT_OUTPUT 91141 typedef ma_int16 ma_dr_mp3d_sample_t; 91142 static ma_int16 ma_dr_mp3d_scale_pcm(float sample) 91143 { 91144 ma_int16 s; 91145 #if MA_DR_MP3_HAVE_ARMV6 91146 ma_int32 s32 = (ma_int32)(sample + .5f); 91147 s32 -= (s32 < 0); 91148 s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); 91149 #else 91150 if (sample >= 32766.5) return (ma_int16) 32767; 91151 if (sample <= -32767.5) return (ma_int16)-32768; 91152 s = (ma_int16)(sample + .5f); 91153 s -= (s < 0); 91154 #endif 91155 return s; 91156 } 91157 #else 91158 typedef float ma_dr_mp3d_sample_t; 91159 static float ma_dr_mp3d_scale_pcm(float sample) 91160 { 91161 return sample*(1.f/32768.f); 91162 } 91163 #endif 91164 static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) 91165 { 91166 float a; 91167 a = (z[14*64] - z[ 0]) * 29; 91168 a += (z[ 1*64] + z[13*64]) * 213; 91169 a += (z[12*64] - z[ 2*64]) * 459; 91170 a += (z[ 3*64] + z[11*64]) * 2037; 91171 a += (z[10*64] - z[ 4*64]) * 5153; 91172 a += (z[ 5*64] + z[ 9*64]) * 6574; 91173 a += (z[ 8*64] - z[ 6*64]) * 37489; 91174 a += z[ 7*64] * 75038; 91175 pcm[0] = ma_dr_mp3d_scale_pcm(a); 91176 z += 2; 91177 a = z[14*64] * 104; 91178 a += z[12*64] * 1567; 91179 a += z[10*64] * 9727; 91180 a += z[ 8*64] * 64019; 91181 a += z[ 6*64] * -9975; 91182 a += z[ 4*64] * -45; 91183 a += z[ 2*64] * 146; 91184 a += z[ 0*64] * -5; 91185 pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); 91186 } 91187 static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) 91188 { 91189 int i; 91190 float *xr = xl + 576*(nch - 1); 91191 ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); 91192 static const float g_win[] = { 91193 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, 91194 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, 91195 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, 91196 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, 91197 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, 91198 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, 91199 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, 91200 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, 91201 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, 91202 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, 91203 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, 91204 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, 91205 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, 91206 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, 91207 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 91208 }; 91209 float *zlin = lins + 15*64; 91210 const float *w = g_win; 91211 zlin[4*15] = xl[18*16]; 91212 zlin[4*15 + 1] = xr[18*16]; 91213 zlin[4*15 + 2] = xl[0]; 91214 zlin[4*15 + 3] = xr[0]; 91215 zlin[4*31] = xl[1 + 18*16]; 91216 zlin[4*31 + 1] = xr[1 + 18*16]; 91217 zlin[4*31 + 2] = xl[1]; 91218 zlin[4*31 + 3] = xr[1]; 91219 ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); 91220 ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); 91221 ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); 91222 ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); 91223 #if MA_DR_MP3_HAVE_SIMD 91224 if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) 91225 { 91226 #define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); 91227 #define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } 91228 #define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } 91229 #define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } 91230 ma_dr_mp3_f4 a, b; 91231 zlin[4*i] = xl[18*(31 - i)]; 91232 zlin[4*i + 1] = xr[18*(31 - i)]; 91233 zlin[4*i + 2] = xl[1 + 18*(31 - i)]; 91234 zlin[4*i + 3] = xr[1 + 18*(31 - i)]; 91235 zlin[4*i + 64] = xl[1 + 18*(1 + i)]; 91236 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; 91237 zlin[4*i - 64 + 2] = xl[18*(1 + i)]; 91238 zlin[4*i - 64 + 3] = xr[18*(1 + i)]; 91239 MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) 91240 { 91241 #ifndef MA_DR_MP3_FLOAT_OUTPUT 91242 #if MA_DR_MP3_HAVE_SSE 91243 static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; 91244 static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; 91245 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), 91246 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); 91247 dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); 91248 dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); 91249 dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); 91250 dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); 91251 dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); 91252 dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); 91253 dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); 91254 dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); 91255 #else 91256 int16x4_t pcma, pcmb; 91257 a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); 91258 b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); 91259 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); 91260 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); 91261 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); 91262 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); 91263 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); 91264 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); 91265 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); 91266 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); 91267 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); 91268 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); 91269 #endif 91270 #else 91271 #if MA_DR_MP3_HAVE_SSE 91272 static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; 91273 #else 91274 const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); 91275 #endif 91276 a = MA_DR_MP3_VMUL(a, g_scale); 91277 b = MA_DR_MP3_VMUL(b, g_scale); 91278 #if MA_DR_MP3_HAVE_SSE 91279 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); 91280 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); 91281 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); 91282 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); 91283 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); 91284 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); 91285 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); 91286 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); 91287 #else 91288 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); 91289 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); 91290 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); 91291 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); 91292 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); 91293 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); 91294 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); 91295 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); 91296 #endif 91297 #endif 91298 } 91299 } else 91300 #endif 91301 #ifdef MA_DR_MP3_ONLY_SIMD 91302 {} 91303 #else 91304 for (i = 14; i >= 0; i--) 91305 { 91306 #define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; 91307 #define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } 91308 #define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } 91309 #define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } 91310 float a[4], b[4]; 91311 zlin[4*i] = xl[18*(31 - i)]; 91312 zlin[4*i + 1] = xr[18*(31 - i)]; 91313 zlin[4*i + 2] = xl[1 + 18*(31 - i)]; 91314 zlin[4*i + 3] = xr[1 + 18*(31 - i)]; 91315 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; 91316 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; 91317 zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; 91318 zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; 91319 MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) 91320 dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); 91321 dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); 91322 dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); 91323 dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); 91324 dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); 91325 dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); 91326 dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); 91327 dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); 91328 } 91329 #endif 91330 } 91331 static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) 91332 { 91333 int i; 91334 for (i = 0; i < nch; i++) 91335 { 91336 ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); 91337 } 91338 MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); 91339 for (i = 0; i < nbands; i += 2) 91340 { 91341 ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); 91342 } 91343 #ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL 91344 if (nch == 1) 91345 { 91346 for (i = 0; i < 15*64; i += 2) 91347 { 91348 qmf_state[i] = lins[nbands*64 + i]; 91349 } 91350 } else 91351 #endif 91352 { 91353 MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); 91354 } 91355 } 91356 static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) 91357 { 91358 int i, nmatch; 91359 for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) 91360 { 91361 i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); 91362 if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) 91363 return nmatch > 0; 91364 if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) 91365 return 0; 91366 } 91367 return 1; 91368 } 91369 static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) 91370 { 91371 int i, k; 91372 for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) 91373 { 91374 if (ma_dr_mp3_hdr_valid(mp3)) 91375 { 91376 int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); 91377 int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); 91378 for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) 91379 { 91380 if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) 91381 { 91382 int fb = k - ma_dr_mp3_hdr_padding(mp3); 91383 int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); 91384 if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) 91385 continue; 91386 frame_and_padding = k; 91387 frame_bytes = fb; 91388 *free_format_bytes = fb; 91389 } 91390 } 91391 if ((frame_bytes && i + frame_and_padding <= mp3_bytes && 91392 ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || 91393 (!i && frame_and_padding == mp3_bytes)) 91394 { 91395 *ptr_frame_bytes = frame_and_padding; 91396 return i; 91397 } 91398 *free_format_bytes = 0; 91399 } 91400 } 91401 *ptr_frame_bytes = 0; 91402 return mp3_bytes; 91403 } 91404 MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) 91405 { 91406 dec->header[0] = 0; 91407 } 91408 MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) 91409 { 91410 int i = 0, igr, frame_size = 0, success = 1; 91411 const ma_uint8 *hdr; 91412 ma_dr_mp3_bs bs_frame[1]; 91413 ma_dr_mp3dec_scratch scratch; 91414 if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) 91415 { 91416 frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); 91417 if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) 91418 { 91419 frame_size = 0; 91420 } 91421 } 91422 if (!frame_size) 91423 { 91424 MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); 91425 i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); 91426 if (!frame_size || i + frame_size > mp3_bytes) 91427 { 91428 info->frame_bytes = i; 91429 return 0; 91430 } 91431 } 91432 hdr = mp3 + i; 91433 MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); 91434 info->frame_bytes = i + frame_size; 91435 info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; 91436 info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); 91437 info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); 91438 info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); 91439 ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); 91440 if (MA_DR_MP3_HDR_IS_CRC(hdr)) 91441 { 91442 ma_dr_mp3_bs_get_bits(bs_frame, 16); 91443 } 91444 if (info->layer == 3) 91445 { 91446 int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); 91447 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) 91448 { 91449 ma_dr_mp3dec_init(dec); 91450 return 0; 91451 } 91452 success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); 91453 if (success && pcm != NULL) 91454 { 91455 for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) 91456 { 91457 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); 91458 ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); 91459 ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); 91460 } 91461 } 91462 ma_dr_mp3_L3_save_reservoir(dec, &scratch); 91463 } else 91464 { 91465 #ifdef MA_DR_MP3_ONLY_MP3 91466 return 0; 91467 #else 91468 ma_dr_mp3_L12_scale_info sci[1]; 91469 if (pcm == NULL) { 91470 return ma_dr_mp3_hdr_frame_samples(hdr); 91471 } 91472 ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); 91473 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); 91474 for (i = 0, igr = 0; igr < 3; igr++) 91475 { 91476 if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) 91477 { 91478 i = 0; 91479 ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); 91480 ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); 91481 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); 91482 pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); 91483 } 91484 if (bs_frame->pos > bs_frame->limit) 91485 { 91486 ma_dr_mp3dec_init(dec); 91487 return 0; 91488 } 91489 } 91490 #endif 91491 } 91492 return success*ma_dr_mp3_hdr_frame_samples(dec->header); 91493 } 91494 MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) 91495 { 91496 size_t i = 0; 91497 #if MA_DR_MP3_HAVE_SIMD 91498 size_t aligned_count = num_samples & ~7; 91499 for(; i < aligned_count; i+=8) 91500 { 91501 ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); 91502 ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); 91503 ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); 91504 #if MA_DR_MP3_HAVE_SSE 91505 ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); 91506 ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); 91507 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), 91508 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); 91509 out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); 91510 out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); 91511 out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); 91512 out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); 91513 out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); 91514 out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); 91515 out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); 91516 out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); 91517 #else 91518 int16x4_t pcma, pcmb; 91519 a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); 91520 b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); 91521 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); 91522 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); 91523 vst1_lane_s16(out+i , pcma, 0); 91524 vst1_lane_s16(out+i+1, pcma, 1); 91525 vst1_lane_s16(out+i+2, pcma, 2); 91526 vst1_lane_s16(out+i+3, pcma, 3); 91527 vst1_lane_s16(out+i+4, pcmb, 0); 91528 vst1_lane_s16(out+i+5, pcmb, 1); 91529 vst1_lane_s16(out+i+6, pcmb, 2); 91530 vst1_lane_s16(out+i+7, pcmb, 3); 91531 #endif 91532 } 91533 #endif 91534 for(; i < num_samples; i++) 91535 { 91536 float sample = in[i] * 32768.0f; 91537 if (sample >= 32766.5) 91538 out[i] = (ma_int16) 32767; 91539 else if (sample <= -32767.5) 91540 out[i] = (ma_int16)-32768; 91541 else 91542 { 91543 short s = (ma_int16)(sample + .5f); 91544 s -= (s < 0); 91545 out[i] = s; 91546 } 91547 } 91548 } 91549 #ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 91550 #define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 91551 #endif 91552 #define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 91553 #ifndef MA_DR_MP3_DATA_CHUNK_SIZE 91554 #define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) 91555 #endif 91556 #define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) 91557 #define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) 91558 #ifndef MA_DR_MP3_PI_D 91559 #define MA_DR_MP3_PI_D 3.14159265358979323846264 91560 #endif 91561 #define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 91562 static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) 91563 { 91564 return x*(1-a) + y*a; 91565 } 91566 static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) 91567 { 91568 float r0 = (y - x); 91569 float r1 = r0*a; 91570 return x + r1; 91571 } 91572 static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) 91573 { 91574 for (;;) { 91575 if (b == 0) { 91576 break; 91577 } else { 91578 ma_uint32 t = a; 91579 a = b; 91580 b = t % a; 91581 } 91582 } 91583 return a; 91584 } 91585 static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) 91586 { 91587 (void)pUserData; 91588 return MA_DR_MP3_MALLOC(sz); 91589 } 91590 static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) 91591 { 91592 (void)pUserData; 91593 return MA_DR_MP3_REALLOC(p, sz); 91594 } 91595 static void ma_dr_mp3__free_default(void* p, void* pUserData) 91596 { 91597 (void)pUserData; 91598 MA_DR_MP3_FREE(p); 91599 } 91600 static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 91601 { 91602 if (pAllocationCallbacks == NULL) { 91603 return NULL; 91604 } 91605 if (pAllocationCallbacks->onMalloc != NULL) { 91606 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); 91607 } 91608 if (pAllocationCallbacks->onRealloc != NULL) { 91609 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); 91610 } 91611 return NULL; 91612 } 91613 static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) 91614 { 91615 if (pAllocationCallbacks == NULL) { 91616 return NULL; 91617 } 91618 if (pAllocationCallbacks->onRealloc != NULL) { 91619 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); 91620 } 91621 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { 91622 void* p2; 91623 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); 91624 if (p2 == NULL) { 91625 return NULL; 91626 } 91627 if (p != NULL) { 91628 MA_DR_MP3_COPY_MEMORY(p2, p, szOld); 91629 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 91630 } 91631 return p2; 91632 } 91633 return NULL; 91634 } 91635 static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 91636 { 91637 if (p == NULL || pAllocationCallbacks == NULL) { 91638 return; 91639 } 91640 if (pAllocationCallbacks->onFree != NULL) { 91641 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); 91642 } 91643 } 91644 static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) 91645 { 91646 if (pAllocationCallbacks != NULL) { 91647 return *pAllocationCallbacks; 91648 } else { 91649 ma_allocation_callbacks allocationCallbacks; 91650 allocationCallbacks.pUserData = NULL; 91651 allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; 91652 allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; 91653 allocationCallbacks.onFree = ma_dr_mp3__free_default; 91654 return allocationCallbacks; 91655 } 91656 } 91657 static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) 91658 { 91659 size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); 91660 pMP3->streamCursor += bytesRead; 91661 return bytesRead; 91662 } 91663 static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) 91664 { 91665 MA_DR_MP3_ASSERT(offset >= 0); 91666 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { 91667 return MA_FALSE; 91668 } 91669 if (origin == ma_dr_mp3_seek_origin_start) { 91670 pMP3->streamCursor = (ma_uint64)offset; 91671 } else { 91672 pMP3->streamCursor += offset; 91673 } 91674 return MA_TRUE; 91675 } 91676 static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) 91677 { 91678 if (offset <= 0x7FFFFFFF) { 91679 return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); 91680 } 91681 if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { 91682 return MA_FALSE; 91683 } 91684 offset -= 0x7FFFFFFF; 91685 while (offset > 0) { 91686 if (offset <= 0x7FFFFFFF) { 91687 if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { 91688 return MA_FALSE; 91689 } 91690 offset = 0; 91691 } else { 91692 if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { 91693 return MA_FALSE; 91694 } 91695 offset -= 0x7FFFFFFF; 91696 } 91697 } 91698 return MA_TRUE; 91699 } 91700 static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) 91701 { 91702 ma_uint32 pcmFramesRead = 0; 91703 MA_DR_MP3_ASSERT(pMP3 != NULL); 91704 MA_DR_MP3_ASSERT(pMP3->onRead != NULL); 91705 if (pMP3->atEnd) { 91706 return 0; 91707 } 91708 for (;;) { 91709 ma_dr_mp3dec_frame_info info; 91710 if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { 91711 size_t bytesRead; 91712 if (pMP3->pData != NULL) { 91713 MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); 91714 } 91715 pMP3->dataConsumed = 0; 91716 if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { 91717 ma_uint8* pNewData; 91718 size_t newDataCap; 91719 newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; 91720 pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); 91721 if (pNewData == NULL) { 91722 return 0; 91723 } 91724 pMP3->pData = pNewData; 91725 pMP3->dataCapacity = newDataCap; 91726 } 91727 bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); 91728 if (bytesRead == 0) { 91729 if (pMP3->dataSize == 0) { 91730 pMP3->atEnd = MA_TRUE; 91731 return 0; 91732 } 91733 } 91734 pMP3->dataSize += bytesRead; 91735 } 91736 if (pMP3->dataSize > INT_MAX) { 91737 pMP3->atEnd = MA_TRUE; 91738 return 0; 91739 } 91740 MA_DR_MP3_ASSERT(pMP3->pData != NULL); 91741 MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); 91742 if (pMP3->pData == NULL) { 91743 return 0; 91744 } 91745 pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); 91746 if (info.frame_bytes > 0) { 91747 pMP3->dataConsumed += (size_t)info.frame_bytes; 91748 pMP3->dataSize -= (size_t)info.frame_bytes; 91749 } 91750 if (pcmFramesRead > 0) { 91751 pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); 91752 pMP3->pcmFramesConsumedInMP3Frame = 0; 91753 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; 91754 pMP3->mp3FrameChannels = info.channels; 91755 pMP3->mp3FrameSampleRate = info.hz; 91756 break; 91757 } else if (info.frame_bytes == 0) { 91758 size_t bytesRead; 91759 MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); 91760 pMP3->dataConsumed = 0; 91761 if (pMP3->dataCapacity == pMP3->dataSize) { 91762 ma_uint8* pNewData; 91763 size_t newDataCap; 91764 newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; 91765 pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); 91766 if (pNewData == NULL) { 91767 return 0; 91768 } 91769 pMP3->pData = pNewData; 91770 pMP3->dataCapacity = newDataCap; 91771 } 91772 bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); 91773 if (bytesRead == 0) { 91774 pMP3->atEnd = MA_TRUE; 91775 return 0; 91776 } 91777 pMP3->dataSize += bytesRead; 91778 } 91779 }; 91780 return pcmFramesRead; 91781 } 91782 static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) 91783 { 91784 ma_uint32 pcmFramesRead = 0; 91785 ma_dr_mp3dec_frame_info info; 91786 MA_DR_MP3_ASSERT(pMP3 != NULL); 91787 MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); 91788 if (pMP3->atEnd) { 91789 return 0; 91790 } 91791 for (;;) { 91792 pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); 91793 if (pcmFramesRead > 0) { 91794 pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); 91795 pMP3->pcmFramesConsumedInMP3Frame = 0; 91796 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; 91797 pMP3->mp3FrameChannels = info.channels; 91798 pMP3->mp3FrameSampleRate = info.hz; 91799 break; 91800 } else if (info.frame_bytes > 0) { 91801 pMP3->memory.currentReadPos += (size_t)info.frame_bytes; 91802 } else { 91803 break; 91804 } 91805 } 91806 pMP3->memory.currentReadPos += (size_t)info.frame_bytes; 91807 return pcmFramesRead; 91808 } 91809 static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) 91810 { 91811 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { 91812 return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); 91813 } else { 91814 return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); 91815 } 91816 } 91817 static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) 91818 { 91819 MA_DR_MP3_ASSERT(pMP3 != NULL); 91820 return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); 91821 } 91822 #if 0 91823 static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) 91824 { 91825 ma_uint32 pcmFrameCount; 91826 MA_DR_MP3_ASSERT(pMP3 != NULL); 91827 pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); 91828 if (pcmFrameCount == 0) { 91829 return 0; 91830 } 91831 pMP3->currentPCMFrame += pcmFrameCount; 91832 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; 91833 pMP3->pcmFramesRemainingInMP3Frame = 0; 91834 return pcmFrameCount; 91835 } 91836 #endif 91837 static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 91838 { 91839 MA_DR_MP3_ASSERT(pMP3 != NULL); 91840 MA_DR_MP3_ASSERT(onRead != NULL); 91841 ma_dr_mp3dec_init(&pMP3->decoder); 91842 pMP3->onRead = onRead; 91843 pMP3->onSeek = onSeek; 91844 pMP3->pUserData = pUserData; 91845 pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); 91846 if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { 91847 return MA_FALSE; 91848 } 91849 if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { 91850 ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); 91851 return MA_FALSE; 91852 } 91853 pMP3->channels = pMP3->mp3FrameChannels; 91854 pMP3->sampleRate = pMP3->mp3FrameSampleRate; 91855 return MA_TRUE; 91856 } 91857 MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) 91858 { 91859 if (pMP3 == NULL || onRead == NULL) { 91860 return MA_FALSE; 91861 } 91862 MA_DR_MP3_ZERO_OBJECT(pMP3); 91863 return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); 91864 } 91865 static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) 91866 { 91867 ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; 91868 size_t bytesRemaining; 91869 MA_DR_MP3_ASSERT(pMP3 != NULL); 91870 MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); 91871 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; 91872 if (bytesToRead > bytesRemaining) { 91873 bytesToRead = bytesRemaining; 91874 } 91875 if (bytesToRead > 0) { 91876 MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); 91877 pMP3->memory.currentReadPos += bytesToRead; 91878 } 91879 return bytesToRead; 91880 } 91881 static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) 91882 { 91883 ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; 91884 MA_DR_MP3_ASSERT(pMP3 != NULL); 91885 if (origin == ma_dr_mp3_seek_origin_current) { 91886 if (byteOffset > 0) { 91887 if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { 91888 byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); 91889 } 91890 } else { 91891 if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { 91892 byteOffset = -(int)pMP3->memory.currentReadPos; 91893 } 91894 } 91895 pMP3->memory.currentReadPos += byteOffset; 91896 } else { 91897 if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { 91898 pMP3->memory.currentReadPos = byteOffset; 91899 } else { 91900 pMP3->memory.currentReadPos = pMP3->memory.dataSize; 91901 } 91902 } 91903 return MA_TRUE; 91904 } 91905 MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) 91906 { 91907 if (pMP3 == NULL) { 91908 return MA_FALSE; 91909 } 91910 MA_DR_MP3_ZERO_OBJECT(pMP3); 91911 if (pData == NULL || dataSize == 0) { 91912 return MA_FALSE; 91913 } 91914 pMP3->memory.pData = (const ma_uint8*)pData; 91915 pMP3->memory.dataSize = dataSize; 91916 pMP3->memory.currentReadPos = 0; 91917 return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); 91918 } 91919 #ifndef MA_DR_MP3_NO_STDIO 91920 #include <stdio.h> 91921 #include <wchar.h> 91922 static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) 91923 { 91924 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); 91925 } 91926 static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) 91927 { 91928 return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; 91929 } 91930 MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) 91931 { 91932 ma_bool32 result; 91933 FILE* pFile; 91934 if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { 91935 return MA_FALSE; 91936 } 91937 result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 91938 if (result != MA_TRUE) { 91939 fclose(pFile); 91940 return result; 91941 } 91942 return MA_TRUE; 91943 } 91944 MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) 91945 { 91946 ma_bool32 result; 91947 FILE* pFile; 91948 if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 91949 return MA_FALSE; 91950 } 91951 result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); 91952 if (result != MA_TRUE) { 91953 fclose(pFile); 91954 return result; 91955 } 91956 return MA_TRUE; 91957 } 91958 #endif 91959 MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) 91960 { 91961 if (pMP3 == NULL) { 91962 return; 91963 } 91964 #ifndef MA_DR_MP3_NO_STDIO 91965 if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { 91966 FILE* pFile = (FILE*)pMP3->pUserData; 91967 if (pFile != NULL) { 91968 fclose(pFile); 91969 pMP3->pUserData = NULL; 91970 } 91971 } 91972 #endif 91973 ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); 91974 } 91975 #if defined(MA_DR_MP3_FLOAT_OUTPUT) 91976 static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) 91977 { 91978 ma_uint64 i; 91979 ma_uint64 i4; 91980 ma_uint64 sampleCount4; 91981 i = 0; 91982 sampleCount4 = sampleCount >> 2; 91983 for (i4 = 0; i4 < sampleCount4; i4 += 1) { 91984 float x0 = src[i+0]; 91985 float x1 = src[i+1]; 91986 float x2 = src[i+2]; 91987 float x3 = src[i+3]; 91988 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); 91989 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); 91990 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); 91991 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); 91992 x0 = x0 * 32767.0f; 91993 x1 = x1 * 32767.0f; 91994 x2 = x2 * 32767.0f; 91995 x3 = x3 * 32767.0f; 91996 dst[i+0] = (ma_int16)x0; 91997 dst[i+1] = (ma_int16)x1; 91998 dst[i+2] = (ma_int16)x2; 91999 dst[i+3] = (ma_int16)x3; 92000 i += 4; 92001 } 92002 for (; i < sampleCount; i += 1) { 92003 float x = src[i]; 92004 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); 92005 x = x * 32767.0f; 92006 dst[i] = (ma_int16)x; 92007 } 92008 } 92009 #endif 92010 #if !defined(MA_DR_MP3_FLOAT_OUTPUT) 92011 static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) 92012 { 92013 ma_uint64 i; 92014 for (i = 0; i < sampleCount; i += 1) { 92015 float x = (float)src[i]; 92016 x = x * 0.000030517578125f; 92017 dst[i] = x; 92018 } 92019 } 92020 #endif 92021 static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) 92022 { 92023 ma_uint64 totalFramesRead = 0; 92024 MA_DR_MP3_ASSERT(pMP3 != NULL); 92025 MA_DR_MP3_ASSERT(pMP3->onRead != NULL); 92026 while (framesToRead > 0) { 92027 ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); 92028 if (pBufferOut != NULL) { 92029 #if defined(MA_DR_MP3_FLOAT_OUTPUT) 92030 float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); 92031 float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); 92032 MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); 92033 #else 92034 ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); 92035 ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); 92036 MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); 92037 #endif 92038 } 92039 pMP3->currentPCMFrame += framesToConsume; 92040 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; 92041 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; 92042 totalFramesRead += framesToConsume; 92043 framesToRead -= framesToConsume; 92044 if (framesToRead == 0) { 92045 break; 92046 } 92047 MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); 92048 if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { 92049 break; 92050 } 92051 } 92052 return totalFramesRead; 92053 } 92054 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) 92055 { 92056 if (pMP3 == NULL || pMP3->onRead == NULL) { 92057 return 0; 92058 } 92059 #if defined(MA_DR_MP3_FLOAT_OUTPUT) 92060 return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); 92061 #else 92062 { 92063 ma_int16 pTempS16[8192]; 92064 ma_uint64 totalPCMFramesRead = 0; 92065 while (totalPCMFramesRead < framesToRead) { 92066 ma_uint64 framesJustRead; 92067 ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; 92068 ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; 92069 if (framesToReadNow > framesRemaining) { 92070 framesToReadNow = framesRemaining; 92071 } 92072 framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); 92073 if (framesJustRead == 0) { 92074 break; 92075 } 92076 ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); 92077 totalPCMFramesRead += framesJustRead; 92078 } 92079 return totalPCMFramesRead; 92080 } 92081 #endif 92082 } 92083 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) 92084 { 92085 if (pMP3 == NULL || pMP3->onRead == NULL) { 92086 return 0; 92087 } 92088 #if !defined(MA_DR_MP3_FLOAT_OUTPUT) 92089 return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); 92090 #else 92091 { 92092 float pTempF32[4096]; 92093 ma_uint64 totalPCMFramesRead = 0; 92094 while (totalPCMFramesRead < framesToRead) { 92095 ma_uint64 framesJustRead; 92096 ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; 92097 ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; 92098 if (framesToReadNow > framesRemaining) { 92099 framesToReadNow = framesRemaining; 92100 } 92101 framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); 92102 if (framesJustRead == 0) { 92103 break; 92104 } 92105 ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); 92106 totalPCMFramesRead += framesJustRead; 92107 } 92108 return totalPCMFramesRead; 92109 } 92110 #endif 92111 } 92112 static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) 92113 { 92114 MA_DR_MP3_ASSERT(pMP3 != NULL); 92115 pMP3->pcmFramesConsumedInMP3Frame = 0; 92116 pMP3->pcmFramesRemainingInMP3Frame = 0; 92117 pMP3->currentPCMFrame = 0; 92118 pMP3->dataSize = 0; 92119 pMP3->atEnd = MA_FALSE; 92120 ma_dr_mp3dec_init(&pMP3->decoder); 92121 } 92122 static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) 92123 { 92124 MA_DR_MP3_ASSERT(pMP3 != NULL); 92125 MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); 92126 if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { 92127 return MA_FALSE; 92128 } 92129 ma_dr_mp3_reset(pMP3); 92130 return MA_TRUE; 92131 } 92132 static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) 92133 { 92134 ma_uint64 framesRead; 92135 #if defined(MA_DR_MP3_FLOAT_OUTPUT) 92136 framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); 92137 #else 92138 framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); 92139 #endif 92140 if (framesRead != frameOffset) { 92141 return MA_FALSE; 92142 } 92143 return MA_TRUE; 92144 } 92145 static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) 92146 { 92147 MA_DR_MP3_ASSERT(pMP3 != NULL); 92148 if (frameIndex == pMP3->currentPCMFrame) { 92149 return MA_TRUE; 92150 } 92151 if (frameIndex < pMP3->currentPCMFrame) { 92152 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { 92153 return MA_FALSE; 92154 } 92155 } 92156 MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); 92157 return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); 92158 } 92159 static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) 92160 { 92161 ma_uint32 iSeekPoint; 92162 MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); 92163 *pSeekPointIndex = 0; 92164 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { 92165 return MA_FALSE; 92166 } 92167 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { 92168 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { 92169 break; 92170 } 92171 *pSeekPointIndex = iSeekPoint; 92172 } 92173 return MA_TRUE; 92174 } 92175 static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) 92176 { 92177 ma_dr_mp3_seek_point seekPoint; 92178 ma_uint32 priorSeekPointIndex; 92179 ma_uint16 iMP3Frame; 92180 ma_uint64 leftoverFrames; 92181 MA_DR_MP3_ASSERT(pMP3 != NULL); 92182 MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); 92183 MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); 92184 if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { 92185 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; 92186 } else { 92187 seekPoint.seekPosInBytes = 0; 92188 seekPoint.pcmFrameIndex = 0; 92189 seekPoint.mp3FramesToDiscard = 0; 92190 seekPoint.pcmFramesToDiscard = 0; 92191 } 92192 if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { 92193 return MA_FALSE; 92194 } 92195 ma_dr_mp3_reset(pMP3); 92196 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { 92197 ma_uint32 pcmFramesRead; 92198 ma_dr_mp3d_sample_t* pPCMFrames; 92199 pPCMFrames = NULL; 92200 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { 92201 pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; 92202 } 92203 pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); 92204 if (pcmFramesRead == 0) { 92205 return MA_FALSE; 92206 } 92207 } 92208 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; 92209 leftoverFrames = frameIndex - pMP3->currentPCMFrame; 92210 return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); 92211 } 92212 MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) 92213 { 92214 if (pMP3 == NULL || pMP3->onSeek == NULL) { 92215 return MA_FALSE; 92216 } 92217 if (frameIndex == 0) { 92218 return ma_dr_mp3_seek_to_start_of_stream(pMP3); 92219 } 92220 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { 92221 return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); 92222 } else { 92223 return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); 92224 } 92225 } 92226 MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) 92227 { 92228 ma_uint64 currentPCMFrame; 92229 ma_uint64 totalPCMFrameCount; 92230 ma_uint64 totalMP3FrameCount; 92231 if (pMP3 == NULL) { 92232 return MA_FALSE; 92233 } 92234 if (pMP3->onSeek == NULL) { 92235 return MA_FALSE; 92236 } 92237 currentPCMFrame = pMP3->currentPCMFrame; 92238 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { 92239 return MA_FALSE; 92240 } 92241 totalPCMFrameCount = 0; 92242 totalMP3FrameCount = 0; 92243 for (;;) { 92244 ma_uint32 pcmFramesInCurrentMP3Frame; 92245 pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); 92246 if (pcmFramesInCurrentMP3Frame == 0) { 92247 break; 92248 } 92249 totalPCMFrameCount += pcmFramesInCurrentMP3Frame; 92250 totalMP3FrameCount += 1; 92251 } 92252 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { 92253 return MA_FALSE; 92254 } 92255 if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { 92256 return MA_FALSE; 92257 } 92258 if (pMP3FrameCount != NULL) { 92259 *pMP3FrameCount = totalMP3FrameCount; 92260 } 92261 if (pPCMFrameCount != NULL) { 92262 *pPCMFrameCount = totalPCMFrameCount; 92263 } 92264 return MA_TRUE; 92265 } 92266 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) 92267 { 92268 ma_uint64 totalPCMFrameCount; 92269 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { 92270 return 0; 92271 } 92272 return totalPCMFrameCount; 92273 } 92274 MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) 92275 { 92276 ma_uint64 totalMP3FrameCount; 92277 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { 92278 return 0; 92279 } 92280 return totalMP3FrameCount; 92281 } 92282 static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) 92283 { 92284 float srcRatio; 92285 float pcmFrameCountOutF; 92286 ma_uint32 pcmFrameCountOut; 92287 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; 92288 MA_DR_MP3_ASSERT(srcRatio > 0); 92289 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); 92290 pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; 92291 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; 92292 *pRunningPCMFrameCount += pcmFrameCountOut; 92293 } 92294 typedef struct 92295 { 92296 ma_uint64 bytePos; 92297 ma_uint64 pcmFrameIndex; 92298 } ma_dr_mp3__seeking_mp3_frame_info; 92299 MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) 92300 { 92301 ma_uint32 seekPointCount; 92302 ma_uint64 currentPCMFrame; 92303 ma_uint64 totalMP3FrameCount; 92304 ma_uint64 totalPCMFrameCount; 92305 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { 92306 return MA_FALSE; 92307 } 92308 seekPointCount = *pSeekPointCount; 92309 if (seekPointCount == 0) { 92310 return MA_FALSE; 92311 } 92312 currentPCMFrame = pMP3->currentPCMFrame; 92313 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { 92314 return MA_FALSE; 92315 } 92316 if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { 92317 seekPointCount = 1; 92318 pSeekPoints[0].seekPosInBytes = 0; 92319 pSeekPoints[0].pcmFrameIndex = 0; 92320 pSeekPoints[0].mp3FramesToDiscard = 0; 92321 pSeekPoints[0].pcmFramesToDiscard = 0; 92322 } else { 92323 ma_uint64 pcmFramesBetweenSeekPoints; 92324 ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; 92325 ma_uint64 runningPCMFrameCount = 0; 92326 float runningPCMFrameCountFractionalPart = 0; 92327 ma_uint64 nextTargetPCMFrame; 92328 ma_uint32 iMP3Frame; 92329 ma_uint32 iSeekPoint; 92330 if (seekPointCount > totalMP3FrameCount-1) { 92331 seekPointCount = (ma_uint32)totalMP3FrameCount-1; 92332 } 92333 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); 92334 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { 92335 return MA_FALSE; 92336 } 92337 for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { 92338 ma_uint32 pcmFramesInCurrentMP3FrameIn; 92339 MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); 92340 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; 92341 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; 92342 pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); 92343 if (pcmFramesInCurrentMP3FrameIn == 0) { 92344 return MA_FALSE; 92345 } 92346 ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); 92347 } 92348 nextTargetPCMFrame = 0; 92349 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { 92350 nextTargetPCMFrame += pcmFramesBetweenSeekPoints; 92351 for (;;) { 92352 if (nextTargetPCMFrame < runningPCMFrameCount) { 92353 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; 92354 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; 92355 pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; 92356 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); 92357 break; 92358 } else { 92359 size_t i; 92360 ma_uint32 pcmFramesInCurrentMP3FrameIn; 92361 for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { 92362 mp3FrameInfo[i] = mp3FrameInfo[i+1]; 92363 } 92364 mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; 92365 mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; 92366 pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); 92367 if (pcmFramesInCurrentMP3FrameIn == 0) { 92368 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; 92369 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; 92370 pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; 92371 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); 92372 break; 92373 } 92374 ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); 92375 } 92376 } 92377 } 92378 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { 92379 return MA_FALSE; 92380 } 92381 if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { 92382 return MA_FALSE; 92383 } 92384 } 92385 *pSeekPointCount = seekPointCount; 92386 return MA_TRUE; 92387 } 92388 MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) 92389 { 92390 if (pMP3 == NULL) { 92391 return MA_FALSE; 92392 } 92393 if (seekPointCount == 0 || pSeekPoints == NULL) { 92394 pMP3->seekPointCount = 0; 92395 pMP3->pSeekPoints = NULL; 92396 } else { 92397 pMP3->seekPointCount = seekPointCount; 92398 pMP3->pSeekPoints = pSeekPoints; 92399 } 92400 return MA_TRUE; 92401 } 92402 static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) 92403 { 92404 ma_uint64 totalFramesRead = 0; 92405 ma_uint64 framesCapacity = 0; 92406 float* pFrames = NULL; 92407 float temp[4096]; 92408 MA_DR_MP3_ASSERT(pMP3 != NULL); 92409 for (;;) { 92410 ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; 92411 ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); 92412 if (framesJustRead == 0) { 92413 break; 92414 } 92415 if (framesCapacity < totalFramesRead + framesJustRead) { 92416 ma_uint64 oldFramesBufferSize; 92417 ma_uint64 newFramesBufferSize; 92418 ma_uint64 newFramesCap; 92419 float* pNewFrames; 92420 newFramesCap = framesCapacity * 2; 92421 if (newFramesCap < totalFramesRead + framesJustRead) { 92422 newFramesCap = totalFramesRead + framesJustRead; 92423 } 92424 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); 92425 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); 92426 if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { 92427 break; 92428 } 92429 pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); 92430 if (pNewFrames == NULL) { 92431 ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); 92432 break; 92433 } 92434 pFrames = pNewFrames; 92435 framesCapacity = newFramesCap; 92436 } 92437 MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); 92438 totalFramesRead += framesJustRead; 92439 if (framesJustRead != framesToReadRightNow) { 92440 break; 92441 } 92442 } 92443 if (pConfig != NULL) { 92444 pConfig->channels = pMP3->channels; 92445 pConfig->sampleRate = pMP3->sampleRate; 92446 } 92447 ma_dr_mp3_uninit(pMP3); 92448 if (pTotalFrameCount) { 92449 *pTotalFrameCount = totalFramesRead; 92450 } 92451 return pFrames; 92452 } 92453 static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) 92454 { 92455 ma_uint64 totalFramesRead = 0; 92456 ma_uint64 framesCapacity = 0; 92457 ma_int16* pFrames = NULL; 92458 ma_int16 temp[4096]; 92459 MA_DR_MP3_ASSERT(pMP3 != NULL); 92460 for (;;) { 92461 ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; 92462 ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); 92463 if (framesJustRead == 0) { 92464 break; 92465 } 92466 if (framesCapacity < totalFramesRead + framesJustRead) { 92467 ma_uint64 newFramesBufferSize; 92468 ma_uint64 oldFramesBufferSize; 92469 ma_uint64 newFramesCap; 92470 ma_int16* pNewFrames; 92471 newFramesCap = framesCapacity * 2; 92472 if (newFramesCap < totalFramesRead + framesJustRead) { 92473 newFramesCap = totalFramesRead + framesJustRead; 92474 } 92475 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); 92476 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); 92477 if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { 92478 break; 92479 } 92480 pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); 92481 if (pNewFrames == NULL) { 92482 ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); 92483 break; 92484 } 92485 pFrames = pNewFrames; 92486 framesCapacity = newFramesCap; 92487 } 92488 MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); 92489 totalFramesRead += framesJustRead; 92490 if (framesJustRead != framesToReadRightNow) { 92491 break; 92492 } 92493 } 92494 if (pConfig != NULL) { 92495 pConfig->channels = pMP3->channels; 92496 pConfig->sampleRate = pMP3->sampleRate; 92497 } 92498 ma_dr_mp3_uninit(pMP3); 92499 if (pTotalFrameCount) { 92500 *pTotalFrameCount = totalFramesRead; 92501 } 92502 return pFrames; 92503 } 92504 MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92505 { 92506 ma_dr_mp3 mp3; 92507 if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { 92508 return NULL; 92509 } 92510 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 92511 } 92512 MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92513 { 92514 ma_dr_mp3 mp3; 92515 if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { 92516 return NULL; 92517 } 92518 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); 92519 } 92520 MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92521 { 92522 ma_dr_mp3 mp3; 92523 if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { 92524 return NULL; 92525 } 92526 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 92527 } 92528 MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92529 { 92530 ma_dr_mp3 mp3; 92531 if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { 92532 return NULL; 92533 } 92534 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); 92535 } 92536 #ifndef MA_DR_MP3_NO_STDIO 92537 MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92538 { 92539 ma_dr_mp3 mp3; 92540 if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { 92541 return NULL; 92542 } 92543 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 92544 } 92545 MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) 92546 { 92547 ma_dr_mp3 mp3; 92548 if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { 92549 return NULL; 92550 } 92551 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); 92552 } 92553 #endif 92554 MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) 92555 { 92556 if (pAllocationCallbacks != NULL) { 92557 return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); 92558 } else { 92559 return ma_dr_mp3__malloc_default(sz, NULL); 92560 } 92561 } 92562 MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) 92563 { 92564 if (pAllocationCallbacks != NULL) { 92565 ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); 92566 } else { 92567 ma_dr_mp3__free_default(p, NULL); 92568 } 92569 } 92570 #endif 92571 /* dr_mp3_c end */ 92572 #endif /* MA_DR_MP3_IMPLEMENTATION */ 92573 #endif /* MA_NO_MP3 */ 92574 92575 92576 /* End globally disabled warnings. */ 92577 #if defined(_MSC_VER) 92578 #pragma warning(pop) 92579 #endif 92580 92581 #endif /* miniaudio_c */ 92582 #endif /* MINIAUDIO_IMPLEMENTATION */ 92583 92584 92585 /* 92586 This software is available as a choice of the following licenses. Choose 92587 whichever you prefer. 92588 92589 =============================================================================== 92590 ALTERNATIVE 1 - Public Domain (www.unlicense.org) 92591 =============================================================================== 92592 This is free and unencumbered software released into the public domain. 92593 92594 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 92595 software, either in source code form or as a compiled binary, for any purpose, 92596 commercial or non-commercial, and by any means. 92597 92598 In jurisdictions that recognize copyright laws, the author or authors of this 92599 software dedicate any and all copyright interest in the software to the public 92600 domain. We make this dedication for the benefit of the public at large and to 92601 the detriment of our heirs and successors. We intend this dedication to be an 92602 overt act of relinquishment in perpetuity of all present and future rights to 92603 this software under copyright law. 92604 92605 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92606 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92607 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 92608 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 92609 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 92610 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 92611 92612 For more information, please refer to <http://unlicense.org/> 92613 92614 =============================================================================== 92615 ALTERNATIVE 2 - MIT No Attribution 92616 =============================================================================== 92617 Copyright 2023 David Reid 92618 92619 Permission is hereby granted, free of charge, to any person obtaining a copy of 92620 this software and associated documentation files (the "Software"), to deal in 92621 the Software without restriction, including without limitation the rights to 92622 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 92623 of the Software, and to permit persons to whom the Software is furnished to do 92624 so. 92625 92626 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92627 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92628 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 92629 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 92630 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 92631 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 92632 SOFTWARE. 92633 */