minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

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, &notifications, &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, &notifications, &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(&notification);
  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(&notification);
  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, &currentFrameIndex);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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, &notification);
  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 = &notification;
  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(&notification);
  69046             return result;
  69047         }
  69048 
  69049         ma_resource_manager_inline_notification_wait_and_uninit(&notification);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  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(&notifications);
  69608         ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
  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              = &notifications;
  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 */